Change negotiation of ECHO for dumb 4.2 telnets
[unix-history] / usr / src / libexec / telnetd / termstat.c
CommitLineData
ea139302
PB
1/*
2 * Copyright (c) 1989 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18#ifndef lint
19static char sccsid[] = "@(#)termstat.c 5.1 (Berkeley) %G%";
20#endif /* not lint */
21
22#include "telnetd.h"
23
24/*
25 * local variables
26 */
27#ifdef LINEMODE
28static int _terminit = 0;
29static int def_tspeed = -1, def_rspeed = -1;
30#ifdef TIOCSWINSZ
31static int def_row = 0, def_col = 0;
32#endif
33#endif LINEMODE
34
35#ifdef CRAY2
36int newmap = 1; /* nonzero if \n maps to ^M^J */
37#endif
38
39#ifdef LINEMODE
40/*
41 * localstat
42 *
43 * This function handles all management of linemode.
44 *
45 * Linemode allows the client to do the local editing of data
46 * and send only complete lines to the server. Linemode state is
47 * based on the state of the pty driver. If the pty is set for
48 * external processing, then we can use linemode. Further, if we
49 * can use real linemode, then we can look at the edit control bits
50 * in the pty to determine what editing the client should do.
51 *
52 * Linemode support uses the following state flags to keep track of
53 * current and desired linemode state.
54 * alwayslinemode : true if -l was specified on the telnetd
55 * command line. It means to have linemode on as much as
56 * possible.
57 *
58 * lmodetype: signifies whether the client can
59 * handle real linemode, or if use of kludgeomatic linemode
60 * is preferred. It will be set to one of the following:
61 * REAL_LINEMODE : use linemode option
62 * KLUDGE_LINEMODE : use kludge linemode
63 * NO_LINEMODE : client is ignorant of linemode
64 *
65 * linemode, uselinemode : linemode is true if linemode
66 * is currently on, uselinemode is the state that we wish
67 * to be in. If another function wishes to turn linemode
68 * on or off, it sets or clears uselinemode.
69 *
70 * editmode, useeditmode : like linemode/uselinemode, but
71 * these contain the edit mode states (edit and trapsig).
72 *
73 * The state variables correspond to some of the state information
74 * in the pty.
75 * linemode:
76 * In real linemode, this corresponds to whether the pty
77 * expects external processing of incoming data.
78 * In kludge linemode, this more closely corresponds to the
79 * whether normal processing is on or not. (ICANON in
80 * system V, or COOKED mode in BSD.)
81 * If the -l option was specified (alwayslinemode), then
82 * an attempt is made to force external processing on at
83 * all times.
84 *
85 * The following heuristics are applied to determine linemode
86 * handling within the server.
87 * 1) Early on in starting up the server, an attempt is made
88 * to negotiate the linemode option. If this succeeds
89 * then lmodetype is set to REAL_LINEMODE and all linemode
90 * processing occurs in the context of the linemode option.
91 * 2) If the attempt to negotiate the linemode option failed,
92 * then we try to use kludge linemode. We test for this
93 * capability by sending "do Timing Mark". If a positive
94 * response comes back, then we assume that the client
95 * understands kludge linemode (ech!) and the
96 * lmodetype flag is set to KLUDGE_LINEMODE.
97 * 3) Otherwise, linemode is not supported at all and
98 * lmodetype remains set to NO_LINEMODE (which happens
99 * to be 0 for convenience).
100 * 4) At any time a command arrives that implies a higher
101 * state of linemode support in the client, we move to that
102 * linemode support.
103 *
104 * A short explanation of kludge linemode is in order here.
105 * 1) The heuristic to determine support for kludge linemode
106 * is to send a do timing mark. We assume that a client
107 * that supports timing marks also supports kludge linemode.
108 * A risky proposition at best.
109 * 2) Further negotiation of linemode is done by changing the
110 * the server's state regarding SGA. If server will SGA,
111 * then linemode is off, if server won't SGA, then linemode
112 * is on.
113 */
114localstat()
115{
116 void netflush();
117
118#ifdef CRAY2
119 /*
120 * Keep track of that ol' CR/NL mapping while we're in the
121 * neighborhood.
122 */
123 newmap = tty_isnewmap();
124#endif /* CRAY2 */
125
126 /*
127 * Check for state of BINARY options.
128 */
129 if (tty_isbinaryin()) {
130 if (hiswants[TELOPT_BINARY] == OPT_NO)
131 willoption(TELOPT_BINARY, 1);
132 } else {
133 if (hiswants[TELOPT_BINARY] == OPT_YES)
134 wontoption(TELOPT_BINARY, 1);
135 }
136
137 if (tty_isbinaryout()) {
138 if (mywants[TELOPT_BINARY] == OPT_NO)
139 dooption(TELOPT_BINARY, 1);
140 } else {
141 if (mywants[TELOPT_BINARY] == OPT_YES)
142 dontoption(TELOPT_BINARY, 1);
143 }
144
145 /*
146 * Check for changes to flow control if client supports it.
147 */
148 if (hisopts[TELOPT_LFLOW] == OPT_YES) {
149 if (tty_flowmode() != flowmode) {
150 flowmode = tty_flowmode();
151 (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB,
152 TELOPT_LFLOW, flowmode, IAC, SE);
153 nfrontp += 6;
154 }
155 }
156
157 /*
158 * Check linemode on/off state
159 */
160 uselinemode = tty_linemode();
161
162 /*
163 * If alwayslinemode is on, and pty is changing to turn it off, then
164 * force linemode back on.
165 */
166 if (alwayslinemode && linemode && !uselinemode) {
167 uselinemode = 1;
168 tty_setlinemode(uselinemode);
169 }
170
171# ifdef KLUDGELINEMODE
172 /*
173 * If using kludge linemode and linemode is desired, it can't
174 * be done if normal line editing is not available on the
175 * pty. This becomes the test for linemode on/off when
176 * using kludge linemode.
177 */
178 if (lmodetype == KLUDGE_LINEMODE && uselinemode && tty_israw())
179 uselinemode = 0;
180# endif /* KLUDGELINEMODE */
181
182 /*
183 * Do echo mode handling as soon as we know what the
184 * linemode is going to be.
185 * If the pty has echo turned off, then tell the client that
186 * the server will echo. If echo is on, then the server
187 * will echo if in character mode, but in linemode the
188 * client should do local echoing. The state machine will
189 * not send anything if it is unnecessary, so don't worry
190 * about that here.
191 */
192 if (tty_isecho() && uselinemode)
193 dontoption(TELOPT_ECHO, 1);
194 else
195 dooption(TELOPT_ECHO, 1);
196
197 /*
198 * If linemode is being turned off, send appropriate
199 * command and then we're all done.
200 */
201 if (!uselinemode && linemode) {
202# ifdef KLUDGELINEMODE
203 if (lmodetype == REAL_LINEMODE)
204# endif /* KLUDGELINEMODE */
205 wontoption(TELOPT_LINEMODE, 1);
206# ifdef KLUDGELINEMODE
207 else if (lmodetype == KLUDGE_LINEMODE)
208 dooption(TELOPT_SGA, 1);
209# endif /* KLUDGELINEMODE */
210 linemode = uselinemode;
211 goto done;
212 }
213
214# ifdef KLUDGELINEMODE
215 /*
216 * If using real linemode check edit modes for possible later use.
217 */
218 if (lmodetype == REAL_LINEMODE) {
219# endif /* KLUDGELINEMODE */
220 useeditmode = 0;
221 if (tty_isediting())
222 useeditmode |= MODE_EDIT;
223 if (tty_istrapsig())
224 useeditmode |= MODE_TRAPSIG;
225# ifdef KLUDGELINEMODE
226 }
227# endif /* KLUDGELINEMODE */
228
229 /*
230 * Negotiate linemode on if pty state has changed to turn it on.
231 * Send appropriate command and send along edit mode, then all done.
232 */
233 if (uselinemode && !linemode) {
234# ifdef KLUDGELINEMODE
235 if (lmodetype == KLUDGE_LINEMODE) {
236 dontoption(TELOPT_SGA, 1);
237 } else if (lmodetype == REAL_LINEMODE) {
238# endif /* KLUDGELINEMODE */
239 willoption(TELOPT_LINEMODE, 1);
240 /* send along edit modes */
241 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
242 TELOPT_LINEMODE, LM_MODE, useeditmode,
243 IAC, SE);
244 nfrontp += 7;
245 editmode = useeditmode;
246# ifdef KLUDGELINEMODE
247 }
248# endif /* KLUDGELINEMODE */
249 linemode = uselinemode;
250 goto done;
251 }
252
253# ifdef KLUDGELINEMODE
254 /*
255 * None of what follows is of any value if not using
256 * real linemode.
257 */
258 if (lmodetype < REAL_LINEMODE)
259 goto done;
260# endif /* KLUDGELINEMODE */
261
262 if (linemode) {
263 /*
264 * If edit mode changed, send edit mode.
265 */
266 if (useeditmode != editmode) {
267 /*
268 * Send along appropriate edit mode mask.
269 */
270 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
271 TELOPT_LINEMODE, LM_MODE, useeditmode,
272 IAC, SE);
273 nfrontp += 7;
274 editmode = useeditmode;
275 }
276
277
278 /*
279 * Check for changes to special characters in use.
280 */
281 start_slc(0);
282 check_slc();
283 end_slc(0);
284 }
285
286done:
287 /*
288 * Some things should be deferred until after the pty state has
289 * been set by the local process. Do those things that have been
290 * deferred now. This only happens once.
291 */
292 if (_terminit == 0) {
293 _terminit = 1;
294 defer_terminit();
295 }
296
297 netflush();
298 set_termbuf();
299 return;
300
301} /* end of localstat */
302#endif /* LINEMODE */
303
304
305/*
306 * clientstat
307 *
308 * Process linemode related requests from the client.
309 * Client can request a change to only one of linemode, editmode or slc's
310 * at a time, and if using kludge linemode, then only linemode may be
311 * affected.
312 */
313clientstat(code, parm1, parm2)
314register int code, parm1, parm2;
315{
316 void netflush();
317
318 /*
319 * Get a copy of terminal characteristics.
320 */
321 init_termbuf();
322
323 /*
324 * Process request from client. code tells what it is.
325 */
326 switch (code) {
327#ifdef LINEMODE
328 case TELOPT_LINEMODE:
329 /*
330 * Don't do anything unless client is asking us to change
331 * modes.
332 */
333 uselinemode = (parm1 == WILL);
334 if (uselinemode != linemode) {
335# ifdef KLUDGELINEMODE
336 /*
337 * If using kludge linemode, make sure that
338 * we can do what the client asks.
339 * We can not turn off linemode if alwayslinemode
340 * or if the ICANON bit is set.
341 */
342 if (lmodetype == KLUDGE_LINEMODE) {
343 if (alwayslinemode || tty_isediting()) {
344 uselinemode = 1;
345 }
346 }
347
348 /*
349 * Quit now if we can't do it.
350 */
351 if (uselinemode == linemode)
352 return;
353
354 /*
355 * If using real linemode and linemode is being
356 * turned on, send along the edit mode mask.
357 */
358 if (lmodetype == REAL_LINEMODE && uselinemode)
359# else /* KLUDGELINEMODE */
360 if (uselinemode)
361# endif /* KLUDGELINEMODE */
362 {
363 useeditmode = 0;
364 if (tty_isediting())
365 useeditmode |= MODE_EDIT;
366 if (tty_istrapsig)
367 useeditmode |= MODE_TRAPSIG;
368 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
369 SB, TELOPT_LINEMODE, LM_MODE,
370 useeditmode, IAC, SE);
371 nfrontp += 7;
372 editmode = useeditmode;
373 }
374
375
376 tty_setlinemode(uselinemode);
377
378 linemode = uselinemode;
379
380 }
381 break;
382
383 case LM_MODE:
384 {
385 register int mode, sig, ack;
386
387 /*
388 * Client has sent along a mode mask. If it agrees with
389 * what we are currently doing, ignore it; if not, it could
390 * be viewed as a request to change. Note that the server
391 * will change to the modes in an ack if it is different from
392 * what we currently have, but we will not ack the ack.
393 */
394 useeditmode &= MODE_MASK;
395 ack = (useeditmode & MODE_ACK);
396 useeditmode &= ~MODE_ACK;
397
398 if (useeditmode != editmode) {
399 mode = (useeditmode & MODE_EDIT);
400 sig = (useeditmode & MODE_TRAPSIG);
401
402 if (mode != (editmode & LM_MODE)) {
403 tty_setedit(mode);
404 }
405 if (sig != (editmode & MODE_TRAPSIG)) {
406 tty_setsig(sig);
407 }
408
409 set_termbuf();
410
411 if (!ack) {
412 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
413 SB, TELOPT_LINEMODE, LM_MODE,
414 useeditmode|MODE_ACK,
415 IAC, SE);
416 nfrontp += 7;
417 }
418
419 editmode = useeditmode;
420 }
421
422 break;
423
424 } /* end of case LM_MODE */
425#endif /* LINEMODE */
426
427 case TELOPT_NAWS:
428#ifdef TIOCSWINSZ
429 {
430 struct winsize ws;
431
432#ifdef LINEMODE
433 /*
434 * Defer changing window size until after terminal is
435 * initialized.
436 */
437 if (terminit() == 0) {
438 def_col = parm1;
439 def_row = parm1;
440 return;
441 }
442#endif /* LINEMODE */
443
444 /*
445 * Change window size as requested by client.
446 */
447
448 ws.ws_col = parm1;
449 ws.ws_row = parm2;
450 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
451 }
452#endif /* TIOCSWINSZ */
453
454 break;
455
456 case TELOPT_TSPEED:
457 {
458#ifdef LINEMODE
459 /*
460 * Defer changing the terminal speed.
461 */
462 if (terminit() == 0) {
463 def_tspeed = parm1;
464 def_rspeed = parm2;
465 return;
466 }
467#endif /* LINEMODE */
468 /*
469 * Change terminal speed as requested by client.
470 */
471 tty_tspeed(parm1);
472 tty_rspeed(parm2);
473 set_termbuf();
474
475 break;
476
477 } /* end of case TELOPT_TSPEED */
478
479 default:
480 /* What? */
481 break;
482 } /* end of switch */
483
484#ifdef CRAY2
485 /*
486 * Just in case of the likely event that we changed the pty state.
487 */
488 rcv_ioctl();
489#endif /* CRAY2 */
490
491 netflush();
492
493} /* end of clientstat */
494
495#ifdef CRAY2
496termstat()
497{
498 needtermstat = 1;
499}
500
501_termstat()
502{
503 needtermstat = 0;
504 init_termbuf();
505 localstat();
506 rcv_ioctl();
507}
508#endif /* CRAY2 */
509
510#ifdef LINEMODE
511/*
512 * defer_terminit
513 *
514 * Some things should not be done until after the login process has started
515 * and all the pty modes are set to what they are supposed to be. This
516 * function is called when the pty state has been processed for the first time.
517 * It calls other functions that do things that were deferred in each module.
518 */
519defer_terminit()
520{
521
522 /*
523 * local stuff that got deferred.
524 */
525 if (def_tspeed != -1) {
526 clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
527 def_tspeed = def_rspeed = 0;
528 }
529
530#ifdef TIOCSWINSZ
531 if (def_col || def_row) {
532 struct winsize ws;
533
534 ws.ws_col = def_col;
535 ws.ws_row = def_row;
536 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
537 }
538#endif
539
540 /*
541 * The only other module that currently defers anything.
542 */
543 deferslc();
544
545} /* end of defer_terminit */
546
547/*
548 * terminit
549 *
550 * Returns true if the pty state has been processed yet.
551 */
552int terminit()
553{
554 return _terminit;
555
556} /* end of terminit */
557#endif /* LINEMODE */