root/src/noit_console_telnet.c

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

integrate a full-on telnet server... hey, why not?

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2007
5  *      OmnitTI Computer Consulting, Inc.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by the University of
18  *      California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "noit_defines.h"
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <sys/ioctl.h>
40 #include <sys/ioctl_compat.h>
41 #include <arpa/telnet.h>
42 #include "noit_console.h"
43 #include "noit_console_telnet.h"
44 #include "utils/noit_hash.h"
45
46 /*
47 RCSID("$Id: state.c,v 1.14.12.1 2004/06/21 08:21:58 lha Exp $");
48 */
49
50 unsigned char   doopt[] = { IAC, DO, '%', 'c', 0 };
51 unsigned char   dont[] = { IAC, DONT, '%', 'c', 0 };
52 unsigned char   will[] = { IAC, WILL, '%', 'c', 0 };
53 unsigned char   wont[] = { IAC, WONT, '%', 'c', 0 };
54 int     not42 = 1;
55
56 /*
57  * Buffer for sub-options, and macros
58  * for suboptions buffer manipulations
59  */
60
61 #define subbuffer ncct->telnet->_subbuffer
62 #define subpointer ncct->telnet->_subpointer
63 #define subend ncct->telnet->_subend
64 #define subsave ncct->telnet->_subsave
65 #define slctab ncct->telnet->_slctab
66 #define pty ncct->pty_master
67 #define do_dont_resp ncct->telnet->_do_dont_resp
68 #define will_wont_resp ncct->telnet->_will_wont_resp
69 #define termbuf ncct->telnet->_termbuf
70 #define termbuf2 ncct->telnet->_termbuf2
71 #define SYNCHing ncct->telnet->_SYNCHing
72 #define terminalname ncct->telnet->_terminalname
73 #define flowmode ncct->telnet->_flowmode
74 #define restartany ncct->telnet->_restartany
75 #define def_tspeed ncct->telnet->_def_tspeed
76 #define def_rspeed ncct->telnet->_def_rspeed
77 #define def_row ncct->telnet->_def_row
78 #define def_col ncct->telnet->_def_col
79
80 #define settimer(x)     (ncct->telnet->_clocks.x = ++ncct->telnet->_clocks.system)
81 #define sequenceIs(x,y) (ncct->telnet->_clocks.x < ncct->telnet->_clocks.y)
82
83 #define dooption(a) noit_console_telnet_dooption(ncct,a)
84 #define dontoption(a) noit_console_telnet_dontoption(ncct,a)
85 #define willoption(a) noit_console_telnet_willoption(ncct,a)
86 #define wontoption(a) noit_console_telnet_wontoption(ncct,a)
87 #define send_do(a,i) noit_console_telnet_send_do(ncct,a,i)
88 #define send_dont(a,i) noit_console_telnet_send_dont(ncct,a,i)
89 #define send_will(a,i) noit_console_telnet_send_will(ncct,a,i)
90 #define send_wont(a,i) noit_console_telnet_send_wont(ncct,a,i)
91 #define send_status() noit_console_telnet_send_status(ncct)
92 #define doclientstat() noit_console_telnet_doclientstat(ncct)
93
94 #ifndef TCSIG
95 # ifdef TIOCSIG
96 #  define TCSIG TIOCSIG
97 # endif
98 #endif
99
100 static void
101 netflush(noit_console_closure_t ncct) {
102   int unused;
103   noit_console_continue_sending(ncct, &unused);
104 }
105 static void
106 suboption(noit_console_closure_t ncct);
107
108 void
109 noit_console_telnet_tty_tspeed(noit_console_closure_t ncct, int val)
110 {
111 #ifdef  DECODE_BAUD
112     struct termspeeds *tp;
113
114     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
115         ;
116     if (tp->speed == -1)        /* back up to last valid value */
117         --tp;
118     cfsetospeed(&termbuf, tp->value);
119 #else   /* DECODE_BUAD */
120     cfsetospeed(&termbuf, val);
121 #endif  /* DECODE_BUAD */
122 }
123
124 void
125 noit_console_telnet_tty_rspeed(noit_console_closure_t ncct, int val)
126 {
127 #ifdef  DECODE_BAUD
128     struct termspeeds *tp;
129
130     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
131         ;
132     if (tp->speed == -1)        /* back up to last valid value */
133         --tp;
134     cfsetispeed(&termbuf, tp->value);
135 #else   /* DECODE_BAUD */
136     cfsetispeed(&termbuf, val);
137 #endif  /* DECODE_BAUD */
138 }
139
140
141 int
142 noit_console_telnet_tty_flowmode(noit_console_closure_t ncct)
143 {
144     return((termbuf.c_iflag & IXON) ? 1 : 0);
145 }
146
147 void
148 noit_console_telnet_tty_binaryin(noit_console_closure_t ncct, int on)
149 {
150     if (on) {
151         termbuf.c_iflag &= ~ISTRIP;
152     } else {
153         termbuf.c_iflag |= ISTRIP;
154     }
155 }
156
157 int
158 noit_console_telnet_tty_restartany(noit_console_closure_t ncct)
159 {
160     return((termbuf.c_iflag & IXANY) ? 1 : 0);
161 }
162
163 void
164 noit_console_telnet_tty_setecho(noit_console_closure_t ncct, int on)
165 {
166     int i;
167     i = ECHO;
168 #ifdef CRMOD
169     i |= CRMOD;
170 #endif
171 #ifndef USE_TERMIO
172     if (on)
173         termbuf.sg.sg_flags |= i;
174     else
175         termbuf.sg.sg_flags &= ~(i);
176 #else
177     if (on)
178         termbuf.c_lflag |= i;
179     else
180         termbuf.c_lflag &= ~i;
181 #endif
182 }
183
184 void
185 noit_console_telnet_tty_binaryout(noit_console_closure_t ncct, int on)
186 {
187     if (on) {
188         termbuf.c_cflag &= ~(CSIZE|PARENB);
189         termbuf.c_cflag |= CS8;
190         termbuf.c_oflag &= ~OPOST;
191     } else {
192         termbuf.c_cflag &= ~CSIZE;
193         termbuf.c_cflag |= CS7|PARENB;
194         termbuf.c_oflag |= OPOST;
195     }
196 }
197
198 void
199 init_termbuf(noit_console_closure_t ncct)
200 {
201 #ifndef USE_TERMIO
202         (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg);
203         (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc);
204         (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc);
205 # ifdef TIOCGSTATE
206         (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state);
207 # endif
208 #else
209         (void) tcgetattr(pty, &termbuf);
210 #endif
211         termbuf2 = termbuf;
212 }
213
214 #if     defined(LINEMODE) && defined(TIOCPKT_IOCTL)
215 void
216 copy_termbuf(noit_console_closure_t ncct, char *cp, size_t len)
217 {
218         if (len > sizeof(termbuf))
219                 len = sizeof(termbuf);
220         memmove((char *)&termbuf, cp, len);
221         termbuf2 = termbuf;
222 }
223 #endif  /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
224
225 void
226 set_termbuf(noit_console_closure_t ncct)
227 {
228         /*
229          * Only make the necessary changes.
230          */
231 #ifndef USE_TERMIO
232         if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg,
233                                                         sizeof(termbuf.sg)))
234                 (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg);
235         if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc,
236                                                         sizeof(termbuf.tc)))
237                 (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc);
238         if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
239                                                         sizeof(termbuf.ltc)))
240                 (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc);
241         if (termbuf.lflags != termbuf2.lflags)
242                 (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags);
243 #else   /* USE_TERMIO */
244         if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
245                 (void) tcsetattr(pty, TCSANOW, &termbuf);
246 #endif  /* USE_TERMIO */
247 }
248
249 /*
250  * flowstat
251  *
252  * Check for changes to flow control
253  */
254 void
255 flowstat(noit_console_closure_t ncct)
256 {
257     if (his_state_is_will(TELOPT_LFLOW)) {
258         if (noit_console_telnet_tty_flowmode(ncct) != flowmode) {
259             flowmode = noit_console_telnet_tty_flowmode(ncct);
260             nc_printf(ncct, "%c%c%c%c%c%c",
261                         IAC, SB, TELOPT_LFLOW,
262                         flowmode ? LFLOW_ON : LFLOW_OFF,
263                         IAC, SE);
264         }
265         if (noit_console_telnet_tty_restartany(ncct) != restartany) {
266             restartany = noit_console_telnet_tty_restartany(ncct);
267             nc_printf(ncct, "%c%c%c%c%c%c",
268                         IAC, SB, TELOPT_LFLOW,
269                         restartany ? LFLOW_RESTART_ANY
270                         : LFLOW_RESTART_XON,
271                         IAC, SE);
272         }
273     }
274 }
275
276 /*
277  * clientstat
278  *
279  * Process linemode related requests from the client.
280  * Client can request a change to only one of linemode, editmode or slc's
281  * at a time, and if using kludge linemode, then only linemode may be
282  * affected.
283  */
284 void
285 clientstat(noit_console_closure_t ncct, int code, int parm1, int parm2)
286 {
287     /*
288      * Get a copy of terminal characteristics.
289      */
290     init_termbuf(ncct);
291
292     /*
293      * Process request from client. code tells what it is.
294      */
295     switch (code) {
296     case TELOPT_NAWS:
297 #ifdef  TIOCSWINSZ
298         {
299             struct winsize ws;
300
301             def_col = parm1;
302             def_row = parm2;
303
304             /*
305              * Change window size as requested by client.
306              */
307
308             ws.ws_col = parm1;
309             ws.ws_row = parm2;
310             ioctl(pty, TIOCSWINSZ, (char *)&ws);
311         }
312 #endif  /* TIOCSWINSZ */
313
314     break;
315
316     case TELOPT_TSPEED:
317         {
318             def_tspeed = parm1;
319             def_rspeed = parm2;
320             /*
321              * Change terminal speed as requested by client.
322              * We set the receive speed first, so that if we can't
323              * store seperate receive and transmit speeds, the transmit
324              * speed will take precedence.
325              */
326             noit_console_telnet_tty_rspeed(ncct, parm2);
327             noit_console_telnet_tty_tspeed(ncct, parm1);
328             set_termbuf(ncct);
329
330             break;
331
332         }  /* end of case TELOPT_TSPEED */
333
334     default:
335         /* What? */
336         break;
337     }  /* end of switch */
338
339     netflush(ncct);
340 }
341
342 /*
343  * spcset(func, valp, valpp)
344  *
345  * This function takes various special characters (func), and
346  * sets *valp to the current value of that character, and
347  * *valpp to point to where in the "termbuf" structure that
348  * value is kept.
349  *
350  * It returns the SLC_ level of support for this function.
351  */
352
353 #ifndef USE_TERMIO
354 int
355 spcset(noit_console_closure_t ncct, int func, cc_t *valp, cc_t **valpp)
356 {
357         switch(func) {
358         case SLC_EOF:
359                 *valp = termbuf.tc.t_eofc;
360                 *valpp = (cc_t *)&termbuf.tc.t_eofc;
361                 return(SLC_VARIABLE);
362         case SLC_EC:
363                 *valp = termbuf.sg.sg_erase;
364                 *valpp = (cc_t *)&termbuf.sg.sg_erase;
365                 return(SLC_VARIABLE);
366         case SLC_EL:
367                 *valp = termbuf.sg.sg_kill;
368                 *valpp = (cc_t *)&termbuf.sg.sg_kill;
369                 return(SLC_VARIABLE);
370         case SLC_IP:
371                 *valp = termbuf.tc.t_intrc;
372                 *valpp = (cc_t *)&termbuf.tc.t_intrc;
373                 return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
374         case SLC_ABORT:
375                 *valp = termbuf.tc.t_quitc;
376                 *valpp = (cc_t *)&termbuf.tc.t_quitc;
377                 return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
378         case SLC_XON:
379                 *valp = termbuf.tc.t_startc;
380                 *valpp = (cc_t *)&termbuf.tc.t_startc;
381                 return(SLC_VARIABLE);
382         case SLC_XOFF:
383                 *valp = termbuf.tc.t_stopc;
384                 *valpp = (cc_t *)&termbuf.tc.t_stopc;
385                 return(SLC_VARIABLE);
386         case SLC_AO:
387                 *valp = termbuf.ltc.t_flushc;
388                 *valpp = (cc_t *)&termbuf.ltc.t_flushc;
389                 return(SLC_VARIABLE);
390         case SLC_SUSP:
391                 *valp = termbuf.ltc.t_suspc;
392                 *valpp = (cc_t *)&termbuf.ltc.t_suspc;
393                 return(SLC_VARIABLE);
394         case SLC_EW:
395                 *valp = termbuf.ltc.t_werasc;
396                 *valpp = (cc_t *)&termbuf.ltc.t_werasc;
397                 return(SLC_VARIABLE);
398         case SLC_RP:
399                 *valp = termbuf.ltc.t_rprntc;
400                 *valpp = (cc_t *)&termbuf.ltc.t_rprntc;
401                 return(SLC_VARIABLE);
402         case SLC_LNEXT:
403                 *valp = termbuf.ltc.t_lnextc;
404                 *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
405                 return(SLC_VARIABLE);
406         case SLC_FORW1:
407                 *valp = termbuf.tc.t_brkc;
408                 *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
409                 return(SLC_VARIABLE);
410         case SLC_BRK:
411         case SLC_SYNCH:
412         case SLC_AYT:
413         case SLC_EOR:
414                 *valp = (cc_t)0;
415                 *valpp = (cc_t *)0;
416                 return(SLC_DEFAULT);
417         default:
418                 *valp = (cc_t)0;
419                 *valpp = (cc_t *)0;
420                 return(SLC_NOSUPPORT);
421         }
422 }
423
424 #else   /* USE_TERMIO */
425
426
427 #define setval(a, b)    *valp = termbuf.c_cc[a]; \
428                         *valpp = &termbuf.c_cc[a]; \
429                         return(b);
430 #define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
431
432 int
433 spcset(noit_console_closure_t ncct, int func, cc_t *valp, cc_t **valpp)
434 {
435         switch(func) {
436         case SLC_EOF:
437                 setval(VEOF, SLC_VARIABLE);
438         case SLC_EC:
439                 setval(VERASE, SLC_VARIABLE);
440         case SLC_EL:
441                 setval(VKILL, SLC_VARIABLE);
442         case SLC_IP:
443                 setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
444         case SLC_ABORT:
445                 setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
446         case SLC_XON:
447 #ifdef  VSTART
448                 setval(VSTART, SLC_VARIABLE);
449 #else
450                 defval(0x13);
451 #endif
452         case SLC_XOFF:
453 #ifdef  VSTOP
454                 setval(VSTOP, SLC_VARIABLE);
455 #else
456                 defval(0x11);
457 #endif
458         case SLC_EW:
459 #ifdef  VWERASE
460                 setval(VWERASE, SLC_VARIABLE);
461 #else
462                 defval(0);
463 #endif
464         case SLC_RP:
465 #ifdef  VREPRINT
466                 setval(VREPRINT, SLC_VARIABLE);
467 #else
468                 defval(0);
469 #endif
470         case SLC_LNEXT:
471 #ifdef  VLNEXT
472                 setval(VLNEXT, SLC_VARIABLE);
473 #else
474                 defval(0);
475 #endif
476         case SLC_AO:
477 #if     !defined(VDISCARD) && defined(VFLUSHO)
478 # define VDISCARD VFLUSHO
479 #endif
480 #ifdef  VDISCARD
481                 setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
482 #else
483                 defval(0);
484 #endif
485         case SLC_SUSP:
486 #ifdef  VSUSP
487                 setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
488 #else
489                 defval(0);
490 #endif
491 #ifdef  VEOL
492         case SLC_FORW1:
493                 setval(VEOL, SLC_VARIABLE);
494 #endif
495 #ifdef  VEOL2
496         case SLC_FORW2:
497                 setval(VEOL2, SLC_VARIABLE);
498 #endif
499         case SLC_AYT:
500 #ifdef  VSTATUS
501                 setval(VSTATUS, SLC_VARIABLE);
502 #else
503                 defval(0);
504 #endif
505
506         case SLC_BRK:
507         case SLC_SYNCH:
508         case SLC_EOR:
509                 defval(0);
510
511         default:
512                 *valp = 0;
513                 *valpp = 0;
514                 return(SLC_NOSUPPORT);
515         }
516 }
517 #endif  /* USE_TERMIO */
518
519 void
520 ptyflush(noit_console_closure_t ncct) {
521   if(ncct->telnet->_pty_fill == 0) return;
522   write(ncct->pty_slave, ncct->telnet->_pty_buf, ncct->telnet->_pty_fill);
523   ncct->telnet->_pty_fill = 0;
524 }
525
526 static void
527 netclear(noit_console_closure_t ncct) {
528   ncct->outbuf_len = 0;
529 }
530
531 static void
532 pty_write(noit_console_closure_t ncct, void *buf, int len) {
533   if(len > sizeof(ncct->telnet->_pty_buf)) {
534     int i;
535     /* split it up */
536     for(i=0; i<len; i+=sizeof(ncct->telnet->_pty_buf)) {
537       pty_write(ncct, buf + i, MIN(sizeof(ncct->telnet->_pty_buf),len-i));
538     }
539   }
540   while(ncct->telnet->_pty_fill + len > sizeof(ncct->telnet->_pty_buf)) {
541     ptyflush(ncct);
542   }
543   memcpy(ncct->telnet->_pty_buf + ncct->telnet->_pty_fill, buf, len);
544   ncct->telnet->_pty_fill += len;
545 }
546
547 /*
548  * Send interrupt to process on other side of pty.
549  * If it is in raw mode, just write NULL;
550  * otherwise, write intr char.
551  */
552 void
553 interrupt(noit_console_closure_t ncct)
554 {
555 #ifdef  TCSIG
556         ptyflush(ncct); /* half-hearted */
557         (void) ioctl(pty, TCSIG, (char *)SIGINT);
558 #else   /* TCSIG */
559         unsigned char ch;
560         ptyflush(ncct); /* half-hearted */
561         init_termbuf(ncct);
562         ch = slctab[SLC_IP].sptr ?
563                         (unsigned char)*slctab[SLC_IP].sptr : '\177';
564         pty_write(ncct, &ch, 1);
565 #endif  /* TCSIG */
566 }
567
568 /*
569  * Send quit to process on other side of pty.
570  * If it is in raw mode, just write NULL;
571  * otherwise, write quit char.
572  */
573 void
574 sendbrk(noit_console_closure_t ncct)
575 {
576 #ifdef  TCSIG
577         ptyflush(ncct); /* half-hearted */
578         (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
579 #else   /* TCSIG */
580         unsigned char ch;
581         ptyflush(ncct); /* half-hearted */
582         init_termbuf(ncct);
583         ch = slctab[SLC_ABORT].sptr ?
584                         (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
585         pty_write(ncct, &ch, 1);
586 #endif  /* TCSIG */
587 }
588
589 void
590 sendsusp(noit_console_closure_t ncct)
591 {
592 #ifdef  SIGTSTP
593 # ifdef TCSIG
594         ptyflush(ncct); /* half-hearted */
595         (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
596 # else  /* TCSIG */
597         unsigned char ch;
598         ptyflush(ncct); /* half-hearted */
599         ch = slctab[SLC_SUSP].sptr ?
600                         (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
601         pty_write(ncct, &ch, 1);
602 # endif /* TCSIG */
603 #endif  /* SIGTSTP */
604 }
605
606 /*
607  * When we get an AYT, if ^T is enabled, use that.  Otherwise,
608  * just send back "[Yes]".
609  */
610 void
611 recv_ayt(noit_console_closure_t ncct)
612 {
613 #if     defined(SIGINFO) && defined(TCSIG)
614         if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
615                 (void) ioctl(pty, TCSIG, (char *)SIGINFO);
616                 return;
617         }
618 #endif
619         nc_printf(ncct, "\r\n[Yes]\r\n");
620 }
621
622 void
623 doeof(noit_console_closure_t ncct)
624 {
625         unsigned char ch;
626         init_termbuf(ncct);
627
628 #if     defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
629         if (!noit_console_telnet_tty_isediting()) {
630                 extern char oldeofc;
631                 pty_write(ncct, &oldeofc, 1);
632                 return;
633         }
634 #endif
635         ch = slctab[SLC_EOF].sptr ?
636                         (unsigned char)*slctab[SLC_EOF].sptr : '\004';
637         pty_write(ncct, &ch, 1);
638 }
639
640 noit_console_telnet_closure_t
641 noit_console_telnet_alloc(noit_console_closure_t ncct) {
642   int i, on;
643   noit_console_telnet_closure_t telnet, tmp;
644   tmp = ncct->telnet;
645
646   ncct->telnet = calloc(1, sizeof(*telnet));
647   subbuffer = malloc(1024*64);
648   subpointer = subbuffer;
649   subend= subbuffer;
650   def_tspeed = -1;
651   def_rspeed = -1;
652   for (i = 1; i <= NSLC; i++) {
653     slctab[i].defset.flag =
654       spcset(ncct, i, &slctab[i].defset.val, &slctab[i].sptr);
655     slctab[i].current.flag = SLC_NOSUPPORT;
656     slctab[i].current.val = 0;
657   }
658   if (my_state_is_wont(TELOPT_SGA))
659     send_will(TELOPT_SGA, 1);
660   send_do(TELOPT_ECHO, 1);
661   send_do(TELOPT_NAWS, 1);
662   send_will(TELOPT_STATUS, 1);
663   flowmode = 1;         /* default flow control state */
664   restartany = -1;      /* uninitialized... */
665   send_do(TELOPT_LFLOW, 1);
666   if (his_want_state_is_will(TELOPT_ECHO)) {
667     willoption(TELOPT_ECHO);
668   }
669   if (my_state_is_wont(TELOPT_ECHO))
670     send_will(TELOPT_ECHO, 1);
671   on = 1;
672
673   telnet = ncct->telnet;
674   ncct->telnet = tmp;
675   return telnet;
676 }
677 void
678 noit_console_telnet_free(noit_console_telnet_closure_t telnet) {
679   free(telnet->_subbuffer);
680   noit_hash_destroy(&telnet->_env, free, free);
681   free(telnet);
682 }
683
684 #define SB_CLEAR()      subpointer = subbuffer
685 #define SB_TERM()       { subend = subpointer; SB_CLEAR(); }
686 #define SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
687     *subpointer++ = (c); \
688                              }
689 #define SB_GET()        ((*subpointer++)&0xff)
690 #define SB_EOF()        (subpointer >= subend)
691 #define SB_LEN()        (subend - subpointer)
692
693 #ifdef  ENV_HACK
694 unsigned char *subsave;
695 #define SB_SAVE()       subsave = subpointer;
696 #define SB_RESTORE()    subpointer = subsave;
697 #endif
698
699
700 /*
701  * State for recv fsm
702  */
703 #define TS_DATA         0       /* base state */
704 #define TS_IAC          1       /* look for double IAC's */
705 #define TS_CR           2       /* CR-LF ->'s CR */
706 #define TS_SB           3       /* throw away begin's... */
707 #define TS_SE           4       /* ...end's (suboption negotiation) */
708 #define TS_WILL         5       /* will option negotiation */
709 #define TS_WONT         6       /* wont -''- */
710 #define TS_DO           7       /* do -''- */
711 #define TS_DONT         8       /* dont -''- */
712
713 int
714 noit_console_telnet_telrcv(noit_console_closure_t ncct,
715                            const void *buf, int buflen)
716 {
717     int c;
718     unsigned char cc;
719     unsigned char *netip = (unsigned char *)buf;
720     int ncc = buflen;
721     static int state = TS_DATA;
722
723     while (ncc > 0) {
724         c = *netip++ & 0377, ncc--;
725 #ifdef ENCRYPTION
726         if (decrypt_input)
727             c = (*decrypt_input)(c);
728 #endif
729         switch (state) {
730
731         case TS_CR:
732             state = TS_DATA;
733             /* Strip off \n or \0 after a \r */
734             if ((c == 0) || (c == '\n')) {
735                 break;
736             }
737             /* FALL THROUGH */
738
739         case TS_DATA:
740             if (c == IAC) {
741                 state = TS_IAC;
742                 break;
743             }
744             /*
745              * We now map \r\n ==> \r for pragmatic reasons.
746              * Many client implementations send \r\n when
747              * the user hits the CarriageReturn key.
748              *
749              * We USED to map \r\n ==> \n, since \r\n says
750              * that we want to be in column 1 of the next
751              * printable line, and \n is the standard
752              * unix way of saying that (\r is only good
753              * if CRMOD is set, which it normally is).
754              */
755             if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
756 #ifdef ENCRYPTION
757                 int nc = *netip;
758                 if (decrypt_input)
759                     nc = (*decrypt_input)(nc & 0xff);
760 #endif
761                 {
762 #ifdef ENCRYPTION
763                     if (decrypt_input)
764                         (void)(*decrypt_input)(-1);
765 #endif
766                     state = TS_CR;
767                 }
768             }
769             cc = (unsigned char)c;
770             pty_write(ncct, &cc, 1);
771             break;
772
773         case TS_IAC:
774         gotiac:                 switch (c) {
775
776             /*
777              * Send the process on the pty side an
778              * interrupt.  Do this with a NULL or
779              * interrupt char; depending on the tty mode.
780              */
781         case IP:
782             interrupt(ncct);
783             break;
784
785         case BREAK:
786             sendbrk(ncct);
787             break;
788
789             /*
790              * Are You There?
791              */
792         case AYT:
793             recv_ayt(ncct);
794             break;
795
796             /*
797              * Abort Output
798              */
799         case AO:
800             {
801                 ptyflush(ncct); /* half-hearted */
802                 init_termbuf(ncct);
803
804                 if (slctab[SLC_AO].sptr &&
805                     *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
806                     cc = (unsigned char)*slctab[SLC_AO].sptr;
807                     pty_write(ncct, &cc, 1);
808                 }
809
810                 netclear(ncct); /* clear buffer back */
811                 nc_printf (ncct, "%c%c", IAC, DM);
812                 /* Wha?
813                 neturg = nfrontp-1;
814                 */
815                 /* off by one XXX */
816                 break;
817             }
818
819         /*
820          * Erase Character and
821          * Erase Line
822          */
823         case EC:
824         case EL:
825             {
826                 cc_t ch;
827
828                 ptyflush(ncct); /* half-hearted */
829                 init_termbuf(ncct);
830                 if (c == EC)
831                     ch = *slctab[SLC_EC].sptr;
832                 else
833                     ch = *slctab[SLC_EL].sptr;
834                 if (ch != (cc_t)(_POSIX_VDISABLE)) {
835                     cc = (unsigned char)ch;
836                     pty_write(ncct, &cc, 1);
837                 }
838                 break;
839             }
840
841         /*
842          * Check for urgent data...
843          */
844         case DM:
845 #ifdef SUPPORT_OOB
846             SYNCHing = stilloob(net);
847             settimer(gotDM);
848 #endif
849             break;
850
851
852             /*
853              * Begin option subnegotiation...
854              */
855         case SB:
856             state = TS_SB;
857             SB_CLEAR();
858             continue;
859
860         case WILL:
861             state = TS_WILL;
862             continue;
863
864         case WONT:
865             state = TS_WONT;
866             continue;
867
868         case DO:
869             state = TS_DO;
870             continue;
871
872         case DONT:
873             state = TS_DONT;
874             continue;
875         case EOR:
876             if (his_state_is_will(TELOPT_EOR))
877                 doeof(ncct);
878             break;
879
880             /*
881              * Handle RFC 10xx Telnet linemode option additions
882              * to command stream (EOF, SUSP, ABORT).
883              */
884         case xEOF:
885             doeof(ncct);
886             break;
887
888         case SUSP:
889             sendsusp(ncct);
890             break;
891
892         case ABORT:
893             sendbrk(ncct);
894             break;
895
896         case IAC:
897             cc = (unsigned char)c;
898             pty_write(ncct, &cc, 1);
899             break;
900         }
901         state = TS_DATA;
902         break;
903
904         case TS_SB:
905             if (c == IAC) {
906                 state = TS_SE;
907             } else {
908                 SB_ACCUM(c);
909             }
910             break;
911
912         case TS_SE:
913             if (c != SE) {
914                 if (c != IAC) {
915                     /*
916                      * bad form of suboption negotiation.
917                      * handle it in such a way as to avoid
918                      * damage to local state.  Parse
919                      * suboption buffer found so far,
920                      * then treat remaining stream as
921                      * another command sequence.
922                      */
923
924                     /* for DIAGNOSTICS */
925                     SB_ACCUM(IAC);
926                     SB_ACCUM(c);
927                     subpointer -= 2;
928
929                     SB_TERM();
930                     suboption(ncct);
931                     state = TS_IAC;
932                     goto gotiac;
933                 }
934                 SB_ACCUM(c);
935                 state = TS_SB;
936             } else {
937                 /* for DIAGNOSTICS */
938                 SB_ACCUM(IAC);
939                 SB_ACCUM(SE);
940                 subpointer -= 2;
941
942                 SB_TERM();
943                 suboption(ncct);        /* handle sub-option */
944                 state = TS_DATA;
945             }
946             break;
947
948         case TS_WILL:
949             willoption(c);
950             state = TS_DATA;
951             continue;
952
953         case TS_WONT:
954             wontoption(c);
955             if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) )
956                 dontoption(c);
957             state = TS_DATA;
958             continue;
959
960         case TS_DO:
961             dooption(c);
962             state = TS_DATA;
963             continue;
964
965         case TS_DONT:
966             dontoption(c);
967             state = TS_DATA;
968             continue;
969
970         default:
971             return -1;
972         }
973     }
974     return 0;
975 }  /* end of telrcv */
976
977 /*
978  * The will/wont/do/dont state machines are based on Dave Borman's
979  * Telnet option processing state machine.
980  *
981  * These correspond to the following states:
982  *      my_state = the last negotiated state
983  *      want_state = what I want the state to go to
984  *      want_resp = how many requests I have sent
985  * All state defaults are negative, and resp defaults to 0.
986  *
987  * When initiating a request to change state to new_state:
988  *
989  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
990  *      do nothing;
991  * } else {
992  *      want_state = new_state;
993  *      send new_state;
994  *      want_resp++;
995  * }
996  *
997  * When receiving new_state:
998  *
999  * if (want_resp) {
1000  *      want_resp--;
1001  *      if (want_resp && (new_state == my_state))
1002  *              want_resp--;
1003  * }
1004  * if ((want_resp == 0) && (new_state != want_state)) {
1005  *      if (ok_to_switch_to new_state)
1006  *              want_state = new_state;
1007  *      else
1008  *              want_resp++;
1009  *      send want_state;
1010  * }
1011  * my_state = new_state;
1012  *
1013  * Note that new_state is implied in these functions by the function itself.
1014  * will and do imply positive new_state, wont and dont imply negative.
1015  *
1016  * Finally, there is one catch.  If we send a negative response to a
1017  * positive request, my_state will be the positive while want_state will
1018  * remain negative.  my_state will revert to negative when the negative
1019  * acknowlegment arrives from the peer.  Thus, my_state generally tells
1020  * us not only the last negotiated state, but also tells us what the peer
1021  * wants to be doing as well.  It is important to understand this difference
1022  * as we may wish to be processing data streams based on our desired state
1023  * (want_state) or based on what the peer thinks the state is (my_state).
1024  *
1025  * This all works fine because if the peer sends a positive request, the data
1026  * that we receive prior to negative acknowlegment will probably be affected
1027  * by the positive state, and we can process it as such (if we can; if we
1028  * can't then it really doesn't matter).  If it is that important, then the
1029  * peer probably should be buffering until this option state negotiation
1030  * is complete.
1031  *
1032  */
1033 void
1034 noit_console_telnet_send_do(noit_console_closure_t ncct, int option, int init)
1035 {
1036     if (init) {
1037         if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
1038             his_want_state_is_will(option))
1039             return;
1040         /*
1041          * Special case for TELOPT_TM:  We send a DO, but pretend
1042          * that we sent a DONT, so that we can send more DOs if
1043          * we want to.
1044          */
1045         if (option == TELOPT_TM)
1046             set_his_want_state_wont(option);
1047         else
1048             set_his_want_state_will(option);
1049         do_dont_resp[option]++;
1050     }
1051     nc_printf(ncct, (const char *)doopt, option);
1052 }
1053
1054 #ifdef  AUTHENTICATION
1055 extern void auth_request(void);
1056 #endif
1057 #ifdef  ENCRYPTION
1058 extern void encrypt_send_support();
1059 #endif
1060
1061 void
1062 noit_console_telnet_willoption(noit_console_closure_t ncct, int option)
1063 {
1064     int changeok = 0;
1065     void (*func)(noit_console_closure_t) = 0;
1066
1067     /*
1068      * process input from peer.
1069      */
1070
1071     if (do_dont_resp[option]) {
1072         do_dont_resp[option]--;
1073         if (do_dont_resp[option] && his_state_is_will(option))
1074             do_dont_resp[option]--;
1075     }
1076     if (do_dont_resp[option] == 0) {
1077         if (his_want_state_is_wont(option)) {
1078             switch (option) {
1079
1080             case TELOPT_BINARY:
1081                 init_termbuf(ncct);
1082                 noit_console_telnet_tty_binaryin(ncct, 1);
1083                 set_termbuf(ncct);
1084                 changeok++;
1085                 break;
1086
1087             case TELOPT_ECHO:
1088                 /*
1089                  * See comments below for more info.
1090                  */
1091                 not42 = 0;      /* looks like a 4.2 system */
1092                 break;
1093
1094             case TELOPT_TM:
1095                 /*
1096                  * We never respond to a WILL TM, and
1097                  * we leave the state WONT.
1098                  */
1099                 break;
1100
1101             case TELOPT_LFLOW:
1102                 /*
1103                  * If we are going to support flow control
1104                  * option, then don't worry peer that we can't
1105                  * change the flow control characters.
1106                  */
1107                 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
1108                 slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
1109                 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
1110                 slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
1111             case TELOPT_TTYPE:
1112             case TELOPT_SGA:
1113             case TELOPT_NAWS:
1114             case TELOPT_TSPEED:
1115             case TELOPT_XDISPLOC:
1116             case TELOPT_NEW_ENVIRON:
1117             case TELOPT_OLD_ENVIRON:
1118                 changeok++;
1119                 break;
1120
1121
1122 #ifdef  AUTHENTICATION
1123             case TELOPT_AUTHENTICATION:
1124                 func = auth_request;
1125                 changeok++;
1126                 break;
1127 #endif
1128
1129 #ifdef  ENCRYPTION
1130             case TELOPT_ENCRYPT:
1131                 func = encrypt_send_support;
1132                 changeok++;
1133                 break;
1134 #endif
1135                        
1136             default:
1137                 break;
1138             }
1139             if (changeok) {
1140                 set_his_want_state_will(option);
1141                 send_do(option, 0);
1142             } else {
1143                 do_dont_resp[option]++;
1144                 send_dont(option, 0);
1145             }
1146         } else {
1147             /*
1148              * Option processing that should happen when
1149              * we receive conformation of a change in
1150              * state that we had requested.
1151              */
1152             switch (option) {
1153             case TELOPT_ECHO:
1154                 not42 = 0;      /* looks like a 4.2 system */
1155                 /*
1156                  * Egads, he responded "WILL ECHO".  Turn
1157                  * it off right now!
1158                  */
1159                 send_dont(option, 1);
1160                 /*
1161                  * "WILL ECHO".  Kludge upon kludge!
1162                  * A 4.2 client is now echoing user input at
1163                  * the tty.  This is probably undesireable and
1164                  * it should be stopped.  The client will
1165                  * respond WONT TM to the DO TM that we send to
1166                  * check for kludge linemode.  When the WONT TM
1167                  * arrives, linemode will be turned off and a
1168                  * change propogated to the pty.  This change
1169                  * will cause us to process the new pty state
1170                  * in localstat(), which will notice that
1171                  * linemode is off and send a WILL ECHO
1172                  * so that we are properly in character mode and
1173                  * all is well.
1174                  */
1175                 break;
1176
1177 #ifdef  AUTHENTICATION
1178             case TELOPT_AUTHENTICATION:
1179                 func = auth_request;
1180                 break;
1181 #endif
1182
1183 #ifdef  ENCRYPTION
1184             case TELOPT_ENCRYPT:
1185                 func = encrypt_send_support;
1186                 break;
1187 #endif
1188
1189             case TELOPT_LFLOW:
1190                 func = flowstat;
1191                 break;
1192             }
1193         }
1194     }
1195     set_his_state_will(option);
1196     if (func)
1197         (*func)(ncct);
1198 }  /* end of willoption */
1199
1200 void
1201 noit_console_telnet_send_dont(noit_console_closure_t ncct, int option, int init)
1202 {
1203     if (init) {
1204         if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
1205             his_want_state_is_wont(option))
1206             return;
1207         set_his_want_state_wont(option);
1208         do_dont_resp[option]++;
1209     }
1210     nc_printf(ncct, (const char *)dont, option);
1211 }
1212
1213 void
1214 noit_console_telnet_wontoption(noit_console_closure_t ncct, int option)
1215 {
1216     /*
1217      * Process client input.
1218          */
1219
1220     if (do_dont_resp[option]) {
1221         do_dont_resp[option]--;
1222         if (do_dont_resp[option] && his_state_is_wont(option))
1223             do_dont_resp[option]--;
1224     }
1225     if (do_dont_resp[option] == 0) {
1226         if (his_want_state_is_will(option)) {
1227             /* it is always ok to change to negative state */
1228             switch (option) {
1229             case TELOPT_ECHO:
1230                 not42 = 1; /* doesn't seem to be a 4.2 system */
1231                 break;
1232
1233             case TELOPT_BINARY:
1234                 init_termbuf(ncct);
1235                 noit_console_telnet_tty_binaryin(ncct, 0);
1236                 set_termbuf(ncct);
1237                 break;
1238
1239             case TELOPT_TM:
1240                 /*
1241                  * If we get a WONT TM, and had sent a DO TM,
1242                  * don't respond with a DONT TM, just leave it
1243                  * as is.  Short circut the state machine to
1244                  * achive this.
1245                  */
1246                 set_his_want_state_wont(TELOPT_TM);
1247                 return;
1248
1249             case TELOPT_LFLOW:
1250                 /*
1251                  * If we are not going to support flow control
1252                  * option, then let peer know that we can't
1253                  * change the flow control characters.
1254                  */
1255                 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
1256                 slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
1257                 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
1258                 slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
1259                 break;
1260
1261 #ifdef AUTHENTICATION
1262             case TELOPT_AUTHENTICATION:
1263                 auth_finished(0, AUTH_REJECT);
1264                 break;
1265 #endif
1266
1267                 /*
1268                  * For options that we might spin waiting for
1269                  * sub-negotiation, if the client turns off the
1270                  * option rather than responding to the request,
1271                  * we have to treat it here as if we got a response
1272                  * to the sub-negotiation, (by updating the timers)
1273                  * so that we'll break out of the loop.
1274                  */
1275             case TELOPT_TTYPE:
1276                 settimer(ttypesubopt);
1277                 break;
1278
1279             case TELOPT_TSPEED:
1280                 settimer(tspeedsubopt);
1281                 break;
1282
1283             case TELOPT_XDISPLOC:
1284                 settimer(xdisplocsubopt);
1285                 break;
1286
1287             case TELOPT_OLD_ENVIRON:
1288                 settimer(oenvironsubopt);
1289                 break;
1290
1291             case TELOPT_NEW_ENVIRON:
1292                 settimer(environsubopt);
1293                 break;
1294
1295             default:
1296                 break;
1297             }
1298             set_his_want_state_wont(option);
1299             if (his_state_is_will(option))
1300                 send_dont(option, 0);
1301         } else {
1302             switch (option) {
1303             case TELOPT_TM:
1304                 break;
1305
1306 #ifdef AUTHENTICATION
1307             case TELOPT_AUTHENTICATION:
1308                 auth_finished(0, AUTH_REJECT);
1309                 break;
1310 #endif
1311             default:
1312                 break;
1313             }
1314         }
1315     }
1316     set_his_state_wont(option);
1317
1318 }  /* end of wontoption */
1319
1320 void
1321 noit_console_telnet_send_will(noit_console_closure_t ncct, int option, int init)
1322 {
1323     if (init) {
1324         if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
1325             my_want_state_is_will(option))
1326             return;
1327         set_my_want_state_will(option);
1328         will_wont_resp[option]++;
1329     }
1330     nc_printf (ncct, (const char *)will, option);
1331 }
1332
1333 /*
1334  * When we get a DONT SGA, we will try once to turn it
1335  * back on.  If the other side responds DONT SGA, we
1336  * leave it at that.  This is so that when we talk to
1337  * clients that understand KLUDGELINEMODE but not LINEMODE,
1338  * we'll keep them in char-at-a-time mode.
1339  */
1340 int turn_on_sga = 0;
1341
1342 void
1343 noit_console_telnet_dooption(noit_console_closure_t ncct, int option)
1344 {
1345     int changeok = 0;
1346
1347     /*
1348      * Process client input.
1349      */
1350
1351     if (will_wont_resp[option]) {
1352         will_wont_resp[option]--;
1353         if (will_wont_resp[option] && my_state_is_will(option))
1354             will_wont_resp[option]--;
1355     }
1356     if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
1357         switch (option) {
1358         case TELOPT_ECHO:
1359             {
1360                 init_termbuf(ncct);
1361                 noit_console_telnet_tty_setecho(ncct, 1);
1362                 set_termbuf(ncct);
1363             }
1364         changeok++;
1365         break;
1366
1367         case TELOPT_BINARY:
1368             init_termbuf(ncct);
1369             noit_console_telnet_tty_binaryout(ncct, 1);
1370             set_termbuf(ncct);
1371             changeok++;
1372             break;
1373
1374         case TELOPT_SGA:
1375             turn_on_sga = 0;
1376             changeok++;
1377             break;
1378
1379         case TELOPT_STATUS:
1380             changeok++;
1381             break;
1382
1383         case TELOPT_TM:
1384             /*
1385              * Special case for TM.  We send a WILL, but
1386              * pretend we sent a WONT.
1387              */
1388             send_will(option, 0);
1389             set_my_want_state_wont(option);
1390             set_my_state_wont(option);
1391             return;
1392
1393         case TELOPT_LOGOUT:
1394             /*
1395              * When we get a LOGOUT option, respond
1396              * with a WILL LOGOUT, make sure that
1397              * it gets written out to the network,
1398              * and then just go away...
1399              */
1400             set_my_want_state_will(TELOPT_LOGOUT);
1401             send_will(TELOPT_LOGOUT, 0);
1402             set_my_state_will(TELOPT_LOGOUT);
1403             netflush(ncct);
1404             return;
1405             break;
1406
1407 #ifdef ENCRYPTION
1408         case TELOPT_ENCRYPT:
1409             changeok++;
1410             break;
1411 #endif
1412         case TELOPT_LINEMODE:
1413         case TELOPT_TTYPE:
1414         case TELOPT_NAWS:
1415         case TELOPT_TSPEED:
1416         case TELOPT_LFLOW:
1417         case TELOPT_XDISPLOC:
1418 #ifdef  TELOPT_ENVIRON
1419         case TELOPT_NEW_ENVIRON:
1420 #endif
1421         case TELOPT_OLD_ENVIRON:
1422         default:
1423             break;
1424         }
1425         if (changeok) {
1426             set_my_want_state_will(option);
1427             send_will(option, 0);
1428         } else {
1429             will_wont_resp[option]++;
1430             send_wont(option, 0);
1431         }
1432     }
1433     set_my_state_will(option);
1434 }  /* end of dooption */
1435
1436 void
1437 noit_console_telnet_send_wont(noit_console_closure_t ncct, int option, int init)
1438 {
1439     if (init) {
1440         if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
1441             my_want_state_is_wont(option))
1442             return;
1443         set_my_want_state_wont(option);
1444         will_wont_resp[option]++;
1445     }
1446     nc_printf (ncct, (const char *)wont, option);
1447 }
1448
1449 void
1450 noit_console_telnet_dontoption(noit_console_closure_t ncct, int option)
1451 {
1452     /*
1453      * Process client input.
1454          */
1455
1456
1457     if (will_wont_resp[option]) {
1458         will_wont_resp[option]--;
1459         if (will_wont_resp[option] && my_state_is_wont(option))
1460             will_wont_resp[option]--;
1461     }
1462     if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
1463         switch (option) {
1464         case TELOPT_BINARY:
1465             init_termbuf(ncct);
1466             noit_console_telnet_tty_binaryout(ncct, 0);
1467             set_termbuf(ncct);
1468             break;
1469
1470         case TELOPT_ECHO:       /* we should stop echoing */
1471             {
1472                 init_termbuf(ncct);
1473                 noit_console_telnet_tty_setecho(ncct, 0);
1474                 set_termbuf(ncct);
1475             }
1476         break;
1477
1478         case TELOPT_SGA:
1479             set_my_want_state_wont(option);
1480             if (my_state_is_will(option))
1481                 send_wont(option, 0);
1482             set_my_state_wont(option);
1483             if (turn_on_sga ^= 1)
1484                 send_will(option, 1);
1485             return;
1486
1487         default:
1488             break;
1489         }
1490
1491         set_my_want_state_wont(option);
1492         if (my_state_is_will(option))
1493             send_wont(option, 0);
1494     }
1495     set_my_state_wont(option);
1496
1497 }  /* end of dontoption */
1498
1499 #ifdef  ENV_HACK
1500 int env_ovar = -1;
1501 int env_ovalue = -1;
1502 #else   /* ENV_HACK */
1503 # define env_ovar OLD_ENV_VAR
1504 # define env_ovalue OLD_ENV_VALUE
1505 #endif  /* ENV_HACK */
1506
1507 /*
1508  * suboption()
1509  *
1510  *      Look at the sub-option buffer, and try to be helpful to the other
1511  * side.
1512  *
1513  *      Currently we recognize:
1514  *
1515  *      Terminal type is
1516  *      Linemode
1517  *      Window size
1518  *      Terminal speed
1519  */
1520 static void
1521 suboption(noit_console_closure_t ncct)
1522 {
1523     int subchar;
1524
1525     subchar = SB_GET();
1526     switch (subchar) {
1527     case TELOPT_TSPEED: {
1528         int xspeed, rspeed;
1529
1530         if (his_state_is_wont(TELOPT_TSPEED))   /* Ignore if option disabled */
1531             break;
1532
1533         settimer(tspeedsubopt);
1534
1535         if (SB_EOF() || SB_GET() != TELQUAL_IS)
1536             return;
1537
1538         xspeed = atoi((char *)subpointer);
1539
1540         while (SB_GET() != ',' && !SB_EOF());
1541         if (SB_EOF())
1542             return;
1543
1544         rspeed = atoi((char *)subpointer);
1545         clientstat(ncct, TELOPT_TSPEED, xspeed, rspeed);
1546
1547         break;
1548
1549     }  /* end of case TELOPT_TSPEED */
1550
1551     case TELOPT_TTYPE: {                /* Yaaaay! */
1552         char *terminaltype;
1553         if (his_state_is_wont(TELOPT_TTYPE))    /* Ignore if option disabled */
1554             break;
1555         settimer(ttypesubopt);
1556
1557         if (SB_EOF() || SB_GET() != TELQUAL_IS) {
1558             return;             /* ??? XXX but, this is the most robust */
1559         }
1560
1561         terminaltype = terminalname;
1562
1563         while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
1564                !SB_EOF()) {
1565             int c;
1566
1567             c = SB_GET();
1568             if (isupper(c)) {
1569                 c = tolower(c);
1570             }
1571             *terminaltype++ = c;    /* accumulate name */
1572         }
1573         *terminaltype = 0;
1574         terminaltype = terminalname;
1575         break;
1576     }  /* end of case TELOPT_TTYPE */
1577
1578     case TELOPT_NAWS: {
1579         int xwinsize, ywinsize;
1580
1581         if (his_state_is_wont(TELOPT_NAWS))     /* Ignore if option disabled */
1582             break;
1583
1584         if (SB_EOF())
1585             return;
1586         xwinsize = SB_GET() << 8;
1587         if (SB_EOF())
1588             return;
1589         xwinsize |= SB_GET();
1590         if (SB_EOF())
1591             return;
1592         ywinsize = SB_GET() << 8;
1593         if (SB_EOF())
1594             return;
1595         ywinsize |= SB_GET();
1596         clientstat(ncct, TELOPT_NAWS, xwinsize, ywinsize);
1597
1598         break;
1599
1600     }  /* end of case TELOPT_NAWS */
1601
1602     case TELOPT_STATUS: {
1603         int mode;
1604
1605         if (SB_EOF())
1606             break;
1607         mode = SB_GET();
1608         switch (mode) {
1609         case TELQUAL_SEND:
1610             if (my_state_is_will(TELOPT_STATUS))
1611                 send_status();
1612             break;
1613
1614         case TELQUAL_IS:
1615             break;
1616
1617         default:
1618             break;
1619         }
1620         break;
1621     }  /* end of case TELOPT_STATUS */
1622
1623     case TELOPT_XDISPLOC: {
1624         if (SB_EOF() || SB_GET() != TELQUAL_IS)
1625             return;
1626         settimer(xdisplocsubopt);
1627         subpointer[SB_LEN()] = '\0';
1628         noit_hash_replace(&ncct->telnet->_env,
1629                           strdup("DISPLAY"), strlen("DISPLAY"),
1630                           strdup((char *)subpointer),
1631                           free, free);
1632         break;
1633     }  /* end of case TELOPT_XDISPLOC */
1634
1635 #ifdef  TELOPT_NEW_ENVIRON
1636     case TELOPT_NEW_ENVIRON:
1637 #endif
1638     case TELOPT_OLD_ENVIRON: {
1639         int c;
1640         char *cp, *varp, *valp;
1641
1642         if (SB_EOF())
1643             return;
1644         c = SB_GET();
1645         if (c == TELQUAL_IS) {
1646             if (subchar == TELOPT_OLD_ENVIRON)
1647                 settimer(oenvironsubopt);
1648             else
1649                 settimer(environsubopt);
1650         } else if (c != TELQUAL_INFO) {
1651             return;
1652         }
1653
1654 #ifdef  TELOPT_NEW_ENVIRON
1655         if (subchar == TELOPT_NEW_ENVIRON) {
1656             while (!SB_EOF()) {
1657                 c = SB_GET();
1658                 if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
1659                     break;
1660             }
1661         } else
1662 #endif
1663             {
1664 #ifdef  ENV_HACK
1665                 /*
1666                  * We only want to do this if we haven't already decided
1667                  * whether or not the other side has its VALUE and VAR
1668                  * reversed.
1669                  */
1670                 if (env_ovar < 0) {
1671                     int last = -1;              /* invalid value */
1672                     int empty = 0;
1673                     int got_var = 0, got_value = 0, got_uservar = 0;
1674
1675                     /*
1676                      * The other side might have its VALUE and VAR values
1677                      * reversed.  To be interoperable, we need to determine
1678                      * which way it is.  If the first recognized character
1679                      * is a VAR or VALUE, then that will tell us what
1680                      * type of client it is.  If the fist recognized
1681                      * character is a USERVAR, then we continue scanning
1682                      * the suboption looking for two consecutive
1683                      * VAR or VALUE fields.  We should not get two
1684                      * consecutive VALUE fields, so finding two
1685                      * consecutive VALUE or VAR fields will tell us
1686                      * what the client is.
1687                      */
1688                     SB_SAVE();
1689                     while (!SB_EOF()) {
1690                         c = SB_GET();
1691                         switch(c) {
1692                         case OLD_ENV_VAR:
1693                             if (last < 0 || last == OLD_ENV_VAR
1694                                 || (empty && (last == OLD_ENV_VALUE)))
1695                                 goto env_ovar_ok;
1696                             got_var++;
1697                             last = OLD_ENV_VAR;
1698                             break;
1699                         case OLD_ENV_VALUE:
1700                             if (last < 0 || last == OLD_ENV_VALUE
1701                                 || (empty && (last == OLD_ENV_VAR)))
1702                                 goto env_ovar_wrong;
1703                             got_value++;
1704                             last = OLD_ENV_VALUE;
1705                             break;
1706                         case ENV_USERVAR:
1707                             /* count strings of USERVAR as one */
1708                             if (last != ENV_USERVAR)
1709                                 got_uservar++;
1710                             if (empty) {
1711                                 if (last == OLD_ENV_VALUE)
1712                                     goto env_ovar_ok;
1713                                 if (last == OLD_ENV_VAR)
1714                                     goto env_ovar_wrong;
1715                             }
1716                             last = ENV_USERVAR;
1717                             break;
1718                         case ENV_ESC:
1719                             if (!SB_EOF())
1720                                 c = SB_GET();
1721                             /* FALL THROUGH */
1722                         default:
1723                             empty = 0;
1724                             continue;
1725                         }
1726                         empty = 1;
1727                     }
1728                     if (empty) {
1729                         if (last == OLD_ENV_VALUE)
1730                             goto env_ovar_ok;
1731                         if (last == OLD_ENV_VAR)
1732                             goto env_ovar_wrong;
1733                     }
1734                     /*
1735                      * Ok, the first thing was a USERVAR, and there
1736                      * are not two consecutive VAR or VALUE commands,
1737                      * and none of the VAR or VALUE commands are empty.
1738                      * If the client has sent us a well-formed option,
1739                      * then the number of VALUEs received should always
1740                      * be less than or equal to the number of VARs and
1741                      * USERVARs received.
1742                      *
1743                      * If we got exactly as many VALUEs as VARs and
1744                      * USERVARs, the client has the same definitions.
1745                      *
1746                      * If we got exactly as many VARs as VALUEs and
1747                      * USERVARS, the client has reversed definitions.
1748                      */
1749                     if (got_uservar + got_var == got_value) {
1750                     env_ovar_ok:
1751                         env_ovar = OLD_ENV_VAR;
1752                         env_ovalue = OLD_ENV_VALUE;
1753                     } else if (got_uservar + got_value == got_var) {
1754                     env_ovar_wrong:
1755                         env_ovar = OLD_ENV_VALUE;
1756                         env_ovalue = OLD_ENV_VAR;
1757                     }
1758                 }
1759                 SB_RESTORE();
1760 #endif
1761
1762                 while (!SB_EOF()) {
1763                     c = SB_GET();
1764                     if ((c == env_ovar) || (c == ENV_USERVAR))
1765                         break;
1766                 }
1767             }
1768
1769         if (SB_EOF())
1770             return;
1771
1772         cp = varp = (char *)subpointer;
1773         valp = 0;
1774
1775         while (!SB_EOF()) {
1776             c = SB_GET();
1777             if (subchar == TELOPT_OLD_ENVIRON) {
1778                 if (c == env_ovar)
1779                     c = NEW_ENV_VAR;
1780                 else if (c == env_ovalue)
1781                     c = NEW_ENV_VALUE;
1782             }
1783             switch (c) {
1784
1785             case NEW_ENV_VALUE:
1786                 *cp = '\0';
1787                 cp = valp = (char *)subpointer;
1788                 break;
1789
1790             case NEW_ENV_VAR:
1791             case ENV_USERVAR:
1792                 *cp = '\0';
1793                 if (valp)
1794                     noit_hash_replace(&ncct->telnet->_env,
1795                                       strdup(varp), strlen(varp),
1796                                       strdup(valp),
1797                                       free, free);
1798                 else
1799                     noit_hash_delete(&ncct->telnet->_env, varp, strlen(varp),
1800                                      free, free);
1801                 cp = varp = (char *)subpointer;
1802                 valp = 0;
1803                 break;
1804
1805             case ENV_ESC:
1806                 if (SB_EOF())
1807                     break;
1808                 c = SB_GET();
1809                 /* FALL THROUGH */
1810             default:
1811                 *cp++ = c;
1812                 break;
1813             }
1814         }
1815         *cp = '\0';
1816         if (valp)
1817             noit_hash_replace(&ncct->telnet->_env,
1818                               strdup(varp), strlen(varp),
1819                               strdup(valp),
1820                               free, free);
1821         else
1822             noit_hash_delete(&ncct->telnet->_env, varp, strlen(varp),
1823                              free, free);
1824         break;
1825     }  /* end of case TELOPT_NEW_ENVIRON */
1826 #ifdef AUTHENTICATION
1827     case TELOPT_AUTHENTICATION:
1828         if (SB_EOF())
1829             break;
1830         switch(SB_GET()) {
1831         case TELQUAL_SEND:
1832         case TELQUAL_REPLY:
1833             /*
1834              * These are sent by us and cannot be sent by
1835              * the client.
1836              */
1837             break;
1838         case TELQUAL_IS:
1839             auth_is(subpointer, SB_LEN());
1840             break;
1841         case TELQUAL_NAME:
1842             auth_name(subpointer, SB_LEN());
1843             break;
1844         }
1845         break;
1846 #endif
1847 #ifdef ENCRYPTION
1848     case TELOPT_ENCRYPT:
1849         if (SB_EOF())
1850             break;
1851         switch(SB_GET()) {
1852         case ENCRYPT_SUPPORT:
1853             encrypt_support(subpointer, SB_LEN());
1854             break;
1855         case ENCRYPT_IS:
1856             encrypt_is(subpointer, SB_LEN());
1857             break;
1858         case ENCRYPT_REPLY:
1859             encrypt_reply(subpointer, SB_LEN());
1860             break;
1861         case ENCRYPT_START:
1862             encrypt_start(subpointer, SB_LEN());
1863             break;
1864         case ENCRYPT_END:
1865             encrypt_end();
1866             break;
1867         case ENCRYPT_REQSTART:
1868             encrypt_request_start(subpointer, SB_LEN());
1869             break;
1870         case ENCRYPT_REQEND:
1871             /*
1872              * We can always send an REQEND so that we cannot
1873              * get stuck encrypting.  We should only get this
1874              * if we have been able to get in the correct mode
1875              * anyhow.
1876              */
1877             encrypt_request_end();
1878             break;
1879         case ENCRYPT_ENC_KEYID:
1880             encrypt_enc_keyid(subpointer, SB_LEN());
1881             break;
1882         case ENCRYPT_DEC_KEYID:
1883             encrypt_dec_keyid(subpointer, SB_LEN());
1884             break;
1885         default:
1886             break;
1887         }
1888         break;
1889 #endif
1890
1891     default:
1892         break;
1893     }  /* end of switch */
1894
1895 }  /* end of suboption */
1896
1897 void
1898 noit_console_telnet_doclientstat(noit_console_closure_t ncct)
1899 {
1900     clientstat(ncct, TELOPT_LINEMODE, WILL, 0);
1901 }
1902
1903 #undef ADD
1904 #define ADD(c)   *ncp++ = c
1905 #define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
1906
1907 void
1908 noit_console_telnet_send_status(noit_console_closure_t ncct)
1909 {
1910     unsigned char statusbuf[256];
1911     unsigned char *ncp;
1912     unsigned char i;
1913
1914     ncp = statusbuf;
1915
1916     netflush(ncct);     /* get rid of anything waiting to go out */
1917
1918     ADD(IAC);
1919     ADD(SB);
1920     ADD(TELOPT_STATUS);
1921     ADD(TELQUAL_IS);
1922
1923     /*
1924      * We check the want_state rather than the current state,
1925      * because if we received a DO/WILL for an option that we
1926      * don't support, and the other side didn't send a DONT/WONT
1927      * in response to our WONT/DONT, then the "state" will be
1928      * WILL/DO, and the "want_state" will be WONT/DONT.  We
1929      * need to go by the latter.
1930      */
1931     for (i = 0; i < (unsigned char)NTELOPTS; i++) {
1932         if (my_want_state_is_will(i)) {
1933             ADD(WILL);
1934             ADD_DATA(i);
1935         }
1936         if (his_want_state_is_will(i)) {
1937             ADD(DO);
1938             ADD_DATA(i);
1939         }
1940     }
1941
1942     if (his_want_state_is_will(TELOPT_LFLOW)) {
1943         ADD(SB);
1944         ADD(TELOPT_LFLOW);
1945         if (flowmode) {
1946             ADD(LFLOW_ON);
1947         } else {
1948             ADD(LFLOW_OFF);
1949         }
1950         ADD(SE);
1951
1952         if (restartany >= 0) {
1953             ADD(SB);
1954             ADD(TELOPT_LFLOW);
1955             if (restartany) {
1956                 ADD(LFLOW_RESTART_ANY);
1957             } else {
1958                 ADD(LFLOW_RESTART_XON);
1959             }
1960             ADD(SE);
1961         }
1962     }
1963
1964
1965     ADD(IAC);
1966     ADD(SE);
1967
1968     nc_write(ncct, statusbuf, ncp - statusbuf);
1969     netflush(ncct);     /* Send it on its way */
1970 }
Note: See TracBrowser for help on using the browser.