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