Remove advertising clause from all that isn't contrib or userland bin.
[dragonfly.git] / libexec / telnetd / termstat.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)termstat.c       8.2 (Berkeley) 5/30/95
30  * $FreeBSD: src/crypto/telnet/telnetd/termstat.c,v 1.4.2.4 2002/04/13 10:59:09 markm Exp $
31  * $DragonFly: src/crypto/telnet/telnetd/termstat.c,v 1.2 2003/06/17 04:24:37 dillon Exp $
32  */
33
34 #include "telnetd.h"
35
36 #ifdef  ENCRYPTION
37 #include <libtelnet/encrypt.h>
38 #endif
39
40 /*
41  * local variables
42  */
43 int def_tspeed = -1, def_rspeed = -1;
44 #ifdef  TIOCSWINSZ
45 int def_row = 0, def_col = 0;
46 #endif
47 #ifdef  LINEMODE
48 static int _terminit = 0;
49 #endif  /* LINEMODE */
50
51 #ifdef  LINEMODE
52 /*
53  * localstat
54  *
55  * This function handles all management of linemode.
56  *
57  * Linemode allows the client to do the local editing of data
58  * and send only complete lines to the server.  Linemode state is
59  * based on the state of the pty driver.  If the pty is set for
60  * external processing, then we can use linemode.  Further, if we
61  * can use real linemode, then we can look at the edit control bits
62  * in the pty to determine what editing the client should do.
63  *
64  * Linemode support uses the following state flags to keep track of
65  * current and desired linemode state.
66  *      alwayslinemode : true if -l was specified on the telnetd
67  *      command line.  It means to have linemode on as much as
68  *      possible.
69  *
70  *      lmodetype: signifies whether the client can
71  *      handle real linemode, or if use of kludgeomatic linemode
72  *      is preferred.  It will be set to one of the following:
73  *              REAL_LINEMODE : use linemode option
74  *              NO_KLUDGE : don't initiate kludge linemode.
75  *              KLUDGE_LINEMODE : use kludge linemode
76  *              NO_LINEMODE : client is ignorant of linemode
77  *
78  *      linemode, uselinemode : linemode is true if linemode
79  *      is currently on, uselinemode is the state that we wish
80  *      to be in.  If another function wishes to turn linemode
81  *      on or off, it sets or clears uselinemode.
82  *
83  *      editmode, useeditmode : like linemode/uselinemode, but
84  *      these contain the edit mode states (edit and trapsig).
85  *
86  * The state variables correspond to some of the state information
87  * in the pty.
88  *      linemode:
89  *              In real linemode, this corresponds to whether the pty
90  *              expects external processing of incoming data.
91  *              In kludge linemode, this more closely corresponds to the
92  *              whether normal processing is on or not.  (ICANON in
93  *              system V, or COOKED mode in BSD.)
94  *              If the -l option was specified (alwayslinemode), then
95  *              an attempt is made to force external processing on at
96  *              all times.
97  *
98  * The following heuristics are applied to determine linemode
99  * handling within the server.
100  *      1) Early on in starting up the server, an attempt is made
101  *         to negotiate the linemode option.  If this succeeds
102  *         then lmodetype is set to REAL_LINEMODE and all linemode
103  *         processing occurs in the context of the linemode option.
104  *      2) If the attempt to negotiate the linemode option failed,
105  *         and the "-k" (don't initiate kludge linemode) isn't set,
106  *         then we try to use kludge linemode.  We test for this
107  *         capability by sending "do Timing Mark".  If a positive
108  *         response comes back, then we assume that the client
109  *         understands kludge linemode (ech!) and the
110  *         lmodetype flag is set to KLUDGE_LINEMODE.
111  *      3) Otherwise, linemode is not supported at all and
112  *         lmodetype remains set to NO_LINEMODE (which happens
113  *         to be 0 for convenience).
114  *      4) At any time a command arrives that implies a higher
115  *         state of linemode support in the client, we move to that
116  *         linemode support.
117  *
118  * A short explanation of kludge linemode is in order here.
119  *      1) The heuristic to determine support for kludge linemode
120  *         is to send a do timing mark.  We assume that a client
121  *         that supports timing marks also supports kludge linemode.
122  *         A risky proposition at best.
123  *      2) Further negotiation of linemode is done by changing the
124  *         the server's state regarding SGA.  If server will SGA,
125  *         then linemode is off, if server won't SGA, then linemode
126  *         is on.
127  */
128 void
129 localstat(void)
130 {
131         int need_will_echo = 0;
132
133         /*
134          * Check for changes to flow control if client supports it.
135          */
136         flowstat();
137
138         /*
139          * Check linemode on/off state
140          */
141         uselinemode = tty_linemode();
142
143         /*
144          * If alwayslinemode is on, and pty is changing to turn it off, then
145          * force linemode back on.
146          */
147         if (alwayslinemode && linemode && !uselinemode) {
148                 uselinemode = 1;
149                 tty_setlinemode(uselinemode);
150         }
151
152         if (uselinemode) {
153                 /*
154                  * Check for state of BINARY options.
155                  *
156                  * We only need to do the binary dance if we are actually going
157                  * to use linemode.  As this confuses some telnet clients
158                  * that don't support linemode, and doesn't gain us
159                  * anything, we don't do it unless we're doing linemode.
160                  * -Crh (henrich@msu.edu)
161                  */
162
163                 if (tty_isbinaryin()) {
164                         if (his_want_state_is_wont(TELOPT_BINARY))
165                                 send_do(TELOPT_BINARY, 1);
166                 } else {
167                         if (his_want_state_is_will(TELOPT_BINARY))
168                                 send_dont(TELOPT_BINARY, 1);
169                 }
170
171                 if (tty_isbinaryout()) {
172                         if (my_want_state_is_wont(TELOPT_BINARY))
173                                 send_will(TELOPT_BINARY, 1);
174                 } else {
175                         if (my_want_state_is_will(TELOPT_BINARY))
176                                 send_wont(TELOPT_BINARY, 1);
177                 }
178         }
179
180 #ifdef  ENCRYPTION
181         /*
182          * If the terminal is not echoing, but editing is enabled,
183          * something like password input is going to happen, so
184          * if we the other side is not currently sending encrypted
185          * data, ask the other side to start encrypting.
186          */
187         if (his_state_is_will(TELOPT_ENCRYPT)) {
188                 static int enc_passwd = 0;
189                 if (uselinemode && !tty_isecho() && tty_isediting()
190                     && (enc_passwd == 0) && !decrypt_input) {
191                         encrypt_send_request_start();
192                         enc_passwd = 1;
193                 } else if (enc_passwd) {
194                         encrypt_send_request_end();
195                         enc_passwd = 0;
196                 }
197         }
198 #endif  /* ENCRYPTION */
199
200         /*
201          * Do echo mode handling as soon as we know what the
202          * linemode is going to be.
203          * If the pty has echo turned off, then tell the client that
204          * the server will echo.  If echo is on, then the server
205          * will echo if in character mode, but in linemode the
206          * client should do local echoing.  The state machine will
207          * not send anything if it is unnecessary, so don't worry
208          * about that here.
209          *
210          * If we need to send the WILL ECHO (because echo is off),
211          * then delay that until after we have changed the MODE.
212          * This way, when the user is turning off both editing
213          * and echo, the client will get editing turned off first.
214          * This keeps the client from going into encryption mode
215          * and then right back out if it is doing auto-encryption
216          * when passwords are being typed.
217          */
218         if (uselinemode) {
219                 if (tty_isecho())
220                         send_wont(TELOPT_ECHO, 1);
221                 else
222                         need_will_echo = 1;
223 #ifdef  KLUDGELINEMODE
224                 if (lmodetype == KLUDGE_OK)
225                         lmodetype = KLUDGE_LINEMODE;
226 #endif
227         }
228
229         /*
230          * If linemode is being turned off, send appropriate
231          * command and then we're all done.
232          */
233          if (!uselinemode && linemode) {
234 # ifdef KLUDGELINEMODE
235                 if (lmodetype == REAL_LINEMODE) {
236 # endif /* KLUDGELINEMODE */
237                         send_dont(TELOPT_LINEMODE, 1);
238 # ifdef KLUDGELINEMODE
239                 } else if (lmodetype == KLUDGE_LINEMODE)
240                         send_will(TELOPT_SGA, 1);
241 # endif /* KLUDGELINEMODE */
242                 send_will(TELOPT_ECHO, 1);
243                 linemode = uselinemode;
244                 goto done;
245         }
246
247 # ifdef KLUDGELINEMODE
248         /*
249          * If using real linemode check edit modes for possible later use.
250          * If we are in kludge linemode, do the SGA negotiation.
251          */
252         if (lmodetype == REAL_LINEMODE) {
253 # endif /* KLUDGELINEMODE */
254                 useeditmode = 0;
255                 if (tty_isediting())
256                         useeditmode |= MODE_EDIT;
257                 if (tty_istrapsig())
258                         useeditmode |= MODE_TRAPSIG;
259                 if (tty_issofttab())
260                         useeditmode |= MODE_SOFT_TAB;
261                 if (tty_islitecho())
262                         useeditmode |= MODE_LIT_ECHO;
263 # ifdef KLUDGELINEMODE
264         } else if (lmodetype == KLUDGE_LINEMODE) {
265                 if (tty_isediting() && uselinemode)
266                         send_wont(TELOPT_SGA, 1);
267                 else
268                         send_will(TELOPT_SGA, 1);
269         }
270 # endif /* KLUDGELINEMODE */
271
272         /*
273          * Negotiate linemode on if pty state has changed to turn it on.
274          * Send appropriate command and send along edit mode, then all done.
275          */
276         if (uselinemode && !linemode) {
277 # ifdef KLUDGELINEMODE
278                 if (lmodetype == KLUDGE_LINEMODE) {
279                         send_wont(TELOPT_SGA, 1);
280                 } else if (lmodetype == REAL_LINEMODE) {
281 # endif /* KLUDGELINEMODE */
282                         send_do(TELOPT_LINEMODE, 1);
283                         /* send along edit modes */
284                         output_data("%c%c%c%c%c%c%c", IAC, SB,
285                                 TELOPT_LINEMODE, LM_MODE, useeditmode,
286                                 IAC, SE);
287                         editmode = useeditmode;
288 # ifdef KLUDGELINEMODE
289                 }
290 # endif /* KLUDGELINEMODE */
291                 linemode = uselinemode;
292                 goto done;
293         }
294
295 # ifdef KLUDGELINEMODE
296         /*
297          * None of what follows is of any value if not using
298          * real linemode.
299          */
300         if (lmodetype < REAL_LINEMODE)
301                 goto done;
302 # endif /* KLUDGELINEMODE */
303
304         if (linemode && his_state_is_will(TELOPT_LINEMODE)) {
305                 /*
306                  * If edit mode changed, send edit mode.
307                  */
308                  if (useeditmode != editmode) {
309                         /*
310                          * Send along appropriate edit mode mask.
311                          */
312                         output_data("%c%c%c%c%c%c%c", IAC, SB,
313                                 TELOPT_LINEMODE, LM_MODE, useeditmode,
314                                 IAC, SE);
315                         editmode = useeditmode;
316                 }
317
318
319                 /*
320                  * Check for changes to special characters in use.
321                  */
322                 start_slc(0);
323                 check_slc();
324                 (void) end_slc(0);
325         }
326
327 done:
328         if (need_will_echo)
329                 send_will(TELOPT_ECHO, 1);
330         /*
331          * Some things should be deferred until after the pty state has
332          * been set by the local process.  Do those things that have been
333          * deferred now.  This only happens once.
334          */
335         if (_terminit == 0) {
336                 _terminit = 1;
337                 defer_terminit();
338         }
339
340         netflush();
341         set_termbuf();
342         return;
343
344 }  /* end of localstat */
345 #endif  /* LINEMODE */
346
347 /*
348  * flowstat
349  *
350  * Check for changes to flow control
351  */
352 void
353 flowstat(void)
354 {
355         if (his_state_is_will(TELOPT_LFLOW)) {
356                 if (tty_flowmode() != flowmode) {
357                         flowmode = tty_flowmode();
358                         output_data("%c%c%c%c%c%c",
359                                         IAC, SB, TELOPT_LFLOW,
360                                         flowmode ? LFLOW_ON : LFLOW_OFF,
361                                         IAC, SE);
362                 }
363                 if (tty_restartany() != restartany) {
364                         restartany = tty_restartany();
365                         output_data("%c%c%c%c%c%c",
366                                         IAC, SB, TELOPT_LFLOW,
367                                         restartany ? LFLOW_RESTART_ANY
368                                                    : LFLOW_RESTART_XON,
369                                         IAC, SE);
370                 }
371         }
372 }
373
374 /*
375  * clientstat
376  *
377  * Process linemode related requests from the client.
378  * Client can request a change to only one of linemode, editmode or slc's
379  * at a time, and if using kludge linemode, then only linemode may be
380  * affected.
381  */
382 void
383 clientstat(int code, int parm1, int parm2)
384 {
385
386         /*
387          * Get a copy of terminal characteristics.
388          */
389         init_termbuf();
390
391         /*
392          * Process request from client. code tells what it is.
393          */
394         switch (code) {
395 #ifdef  LINEMODE
396         case TELOPT_LINEMODE:
397                 /*
398                  * Don't do anything unless client is asking us to change
399                  * modes.
400                  */
401                 uselinemode = (parm1 == WILL);
402                 if (uselinemode != linemode) {
403 # ifdef KLUDGELINEMODE
404                         /*
405                          * If using kludge linemode, make sure that
406                          * we can do what the client asks.
407                          * We can not turn off linemode if alwayslinemode
408                          * and the ICANON bit is set.
409                          */
410                         if (lmodetype == KLUDGE_LINEMODE) {
411                                 if (alwayslinemode && tty_isediting()) {
412                                         uselinemode = 1;
413                                 }
414                         }
415
416                         /*
417                          * Quit now if we can't do it.
418                          */
419                         if (uselinemode == linemode)
420                                 return;
421
422                         /*
423                          * If using real linemode and linemode is being
424                          * turned on, send along the edit mode mask.
425                          */
426                         if (lmodetype == REAL_LINEMODE && uselinemode)
427 # else  /* KLUDGELINEMODE */
428                         if (uselinemode)
429 # endif /* KLUDGELINEMODE */
430                         {
431                                 useeditmode = 0;
432                                 if (tty_isediting())
433                                         useeditmode |= MODE_EDIT;
434                                 if (tty_istrapsig())
435                                         useeditmode |= MODE_TRAPSIG;
436                                 if (tty_issofttab())
437                                         useeditmode |= MODE_SOFT_TAB;
438                                 if (tty_islitecho())
439                                         useeditmode |= MODE_LIT_ECHO;
440                                 output_data("%c%c%c%c%c%c%c", IAC,
441                                         SB, TELOPT_LINEMODE, LM_MODE,
442                                                         useeditmode, IAC, SE);
443                                 editmode = useeditmode;
444                         }
445
446
447                         tty_setlinemode(uselinemode);
448
449                         linemode = uselinemode;
450
451                         if (!linemode)
452                                 send_will(TELOPT_ECHO, 1);
453                 }
454                 break;
455
456         case LM_MODE:
457             {
458                 int ack, changed;
459
460                 /*
461                  * Client has sent along a mode mask.  If it agrees with
462                  * what we are currently doing, ignore it; if not, it could
463                  * be viewed as a request to change.  Note that the server
464                  * will change to the modes in an ack if it is different from
465                  * what we currently have, but we will not ack the ack.
466                  */
467                  useeditmode &= MODE_MASK;
468                  ack = (useeditmode & MODE_ACK);
469                  useeditmode &= ~MODE_ACK;
470
471                  if ((changed = (useeditmode ^ editmode))) {
472                         /*
473                          * This check is for a timing problem.  If the
474                          * state of the tty has changed (due to the user
475                          * application) we need to process that info
476                          * before we write in the state contained in the
477                          * ack!!!  This gets out the new MODE request,
478                          * and when the ack to that command comes back
479                          * we'll set it and be in the right mode.
480                          */
481                         if (ack)
482                                 localstat();
483                         if (changed & MODE_EDIT)
484                                 tty_setedit(useeditmode & MODE_EDIT);
485
486                         if (changed & MODE_TRAPSIG)
487                                 tty_setsig(useeditmode & MODE_TRAPSIG);
488
489                         if (changed & MODE_SOFT_TAB)
490                                 tty_setsofttab(useeditmode & MODE_SOFT_TAB);
491
492                         if (changed & MODE_LIT_ECHO)
493                                 tty_setlitecho(useeditmode & MODE_LIT_ECHO);
494
495                         set_termbuf();
496
497                         if (!ack) {
498                                 output_data("%c%c%c%c%c%c%c", IAC,
499                                         SB, TELOPT_LINEMODE, LM_MODE,
500                                         useeditmode|MODE_ACK,
501                                         IAC, SE);
502                         }
503
504                         editmode = useeditmode;
505                 }
506
507                 break;
508
509             }  /* end of case LM_MODE */
510 #endif  /* LINEMODE */
511
512         case TELOPT_NAWS:
513 #ifdef  TIOCSWINSZ
514             {
515                 struct winsize ws;
516
517                 def_col = parm1;
518                 def_row = parm2;
519 #ifdef  LINEMODE
520                 /*
521                  * Defer changing window size until after terminal is
522                  * initialized.
523                  */
524                 if (terminit() == 0)
525                         return;
526 #endif  /* LINEMODE */
527
528                 /*
529                  * Change window size as requested by client.
530                  */
531
532                 ws.ws_col = parm1;
533                 ws.ws_row = parm2;
534                 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
535             }
536 #endif  /* TIOCSWINSZ */
537
538                 break;
539
540         case TELOPT_TSPEED:
541             {
542                 def_tspeed = parm1;
543                 def_rspeed = parm2;
544 #ifdef  LINEMODE
545                 /*
546                  * Defer changing the terminal speed.
547                  */
548                 if (terminit() == 0)
549                         return;
550 #endif  /* LINEMODE */
551                 /*
552                  * Change terminal speed as requested by client.
553                  * We set the receive speed first, so that if we can't
554                  * store separate receive and transmit speeds, the transmit
555                  * speed will take precedence.
556                  */
557                 tty_rspeed(parm2);
558                 tty_tspeed(parm1);
559                 set_termbuf();
560
561                 break;
562
563             }  /* end of case TELOPT_TSPEED */
564
565         default:
566                 /* What? */
567                 break;
568         }  /* end of switch */
569
570         netflush();
571
572 }  /* end of clientstat */
573
574 #ifdef  LINEMODE
575 /*
576  * defer_terminit
577  *
578  * Some things should not be done until after the login process has started
579  * and all the pty modes are set to what they are supposed to be.  This
580  * function is called when the pty state has been processed for the first time.
581  * It calls other functions that do things that were deferred in each module.
582  */
583 void
584 defer_terminit(void)
585 {
586
587         /*
588          * local stuff that got deferred.
589          */
590         if (def_tspeed != -1) {
591                 clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
592                 def_tspeed = def_rspeed = 0;
593         }
594
595 #ifdef  TIOCSWINSZ
596         if (def_col || def_row) {
597                 struct winsize ws;
598
599                 memset((char *)&ws, 0, sizeof(ws));
600                 ws.ws_col = def_col;
601                 ws.ws_row = def_row;
602                 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
603         }
604 #endif
605
606         /*
607          * The only other module that currently defers anything.
608          */
609         deferslc();
610
611 }  /* end of defer_terminit */
612
613 /*
614  * terminit
615  *
616  * Returns true if the pty state has been processed yet.
617  */
618 int
619 terminit(void)
620 {
621         return(_terminit);
622
623 }  /* end of terminit */
624 #endif  /* LINEMODE */