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