Commit | Line | Data |
---|---|---|
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 | |
19 | static 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 | |
28 | static int _terminit = 0; | |
29 | static int def_tspeed = -1, def_rspeed = -1; | |
30 | #ifdef TIOCSWINSZ | |
31 | static int def_row = 0, def_col = 0; | |
32 | #endif | |
33 | #endif LINEMODE | |
34 | ||
35 | #ifdef CRAY2 | |
36 | int 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 | */ | |
114 | localstat() | |
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 | ||
286 | done: | |
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 | */ | |
313 | clientstat(code, parm1, parm2) | |
314 | register 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 | |
496 | termstat() | |
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 | */ | |
519 | defer_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 | */ | |
552 | int terminit() | |
553 | { | |
554 | return _terminit; | |
555 | ||
556 | } /* end of terminit */ | |
557 | #endif /* LINEMODE */ |