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[] = "@(#)state.c 5.1 (Berkeley) %G%"; | |
20 | #endif /* not lint */ | |
21 | ||
22 | #include "telnetd.h" | |
23 | ||
24 | char doopt[] = { IAC, DO, '%', 'c', 0 }; | |
25 | char dont[] = { IAC, DONT, '%', 'c', 0 }; | |
26 | char will[] = { IAC, WILL, '%', 'c', 0 }; | |
27 | char wont[] = { IAC, WONT, '%', 'c', 0 }; | |
28 | int not42 = 1; | |
29 | ||
30 | /* | |
31 | * Buffer for sub-options, and macros | |
32 | * for suboptions buffer manipulations | |
33 | */ | |
34 | char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; | |
35 | ||
36 | #define SB_CLEAR() subpointer = subbuffer; | |
37 | #define SB_TERM() { subend = subpointer; SB_CLEAR(); } | |
38 | #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ | |
39 | *subpointer++ = (c); \ | |
40 | } | |
41 | #define SB_GET() ((*subpointer++)&0xff) | |
42 | #define SB_EOF() (subpointer >= subend) | |
43 | ||
44 | ||
45 | ||
46 | /* | |
47 | * State for recv fsm | |
48 | */ | |
49 | #define TS_DATA 0 /* base state */ | |
50 | #define TS_IAC 1 /* look for double IAC's */ | |
51 | #define TS_CR 2 /* CR-LF ->'s CR */ | |
52 | #define TS_SB 3 /* throw away begin's... */ | |
53 | #define TS_SE 4 /* ...end's (suboption negotiation) */ | |
54 | #define TS_WILL 5 /* will option negotiation */ | |
55 | #define TS_WONT 6 /* wont " */ | |
56 | #define TS_DO 7 /* do " */ | |
57 | #define TS_DONT 8 /* dont " */ | |
58 | ||
59 | telrcv() | |
60 | { | |
61 | register int c; | |
62 | static int state = TS_DATA; | |
63 | #ifdef CRAY2 | |
64 | char *opfrontp = pfrontp; | |
65 | #endif | |
66 | ||
67 | while (ncc > 0) { | |
68 | if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) | |
69 | break; | |
70 | c = *netip++ & 0377, ncc--; | |
71 | switch (state) { | |
72 | ||
73 | case TS_CR: | |
74 | state = TS_DATA; | |
75 | /* Strip off \n or \0 after a \r */ | |
76 | if ((c == 0) || (c == '\n')) { | |
77 | break; | |
78 | } | |
79 | /* FALL THROUGH */ | |
80 | ||
81 | case TS_DATA: | |
82 | if (c == IAC) { | |
83 | state = TS_IAC; | |
84 | break; | |
85 | } | |
86 | /* | |
87 | * We now map \r\n ==> \r for pragmatic reasons. | |
88 | * Many client implementations send \r\n when | |
89 | * the user hits the CarriageReturn key. | |
90 | * | |
91 | * We USED to map \r\n ==> \n, since \r\n says | |
92 | * that we want to be in column 1 of the next | |
93 | * printable line, and \n is the standard | |
94 | * unix way of saying that (\r is only good | |
95 | * if CRMOD is set, which it normally is). | |
96 | */ | |
97 | if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { | |
98 | /* | |
99 | * If we are operating in linemode, | |
100 | * convert to local end-of-line. | |
101 | */ | |
102 | if ((linemode) && (ncc > 0)&&('\n' == *netip)) { | |
103 | netip++; ncc--; | |
104 | c = '\n'; | |
105 | } else { | |
106 | state = TS_CR; | |
107 | } | |
108 | } | |
109 | *pfrontp++ = c; | |
110 | break; | |
111 | ||
112 | case TS_IAC: | |
113 | gotiac: switch (c) { | |
114 | ||
115 | /* | |
116 | * Send the process on the pty side an | |
117 | * interrupt. Do this with a NULL or | |
118 | * interrupt char; depending on the tty mode. | |
119 | */ | |
120 | case IP: | |
121 | interrupt(); | |
122 | break; | |
123 | ||
124 | case BREAK: | |
125 | sendbrk(); | |
126 | break; | |
127 | ||
128 | /* | |
129 | * Are You There? | |
130 | */ | |
131 | case AYT: | |
132 | (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); | |
133 | nfrontp += 9; | |
134 | break; | |
135 | ||
136 | /* | |
137 | * Abort Output | |
138 | */ | |
139 | case AO: | |
140 | { | |
141 | ptyflush(); /* half-hearted */ | |
142 | init_termbuf(); | |
143 | ||
144 | if (slctab[SLC_AO].sptr && | |
145 | *slctab[SLC_AO].sptr != '\377') { | |
146 | *pfrontp++ = *slctab[SLC_AO].sptr; | |
147 | } | |
148 | ||
149 | netclear(); /* clear buffer back */ | |
150 | *nfrontp++ = IAC; | |
151 | *nfrontp++ = DM; | |
152 | neturg = nfrontp-1; /* off by one XXX */ | |
153 | break; | |
154 | } | |
155 | ||
156 | /* | |
157 | * Erase Character and | |
158 | * Erase Line | |
159 | */ | |
160 | case EC: | |
161 | case EL: | |
162 | { | |
163 | unsigned char ch; | |
164 | ||
165 | ptyflush(); /* half-hearted */ | |
166 | init_termbuf(); | |
167 | ch = (c == EC) ? *slctab[SLC_EC].sptr : | |
168 | *slctab[SLC_EL].sptr; | |
169 | if (ch != '\377') | |
170 | *pfrontp++ = ch; | |
171 | break; | |
172 | } | |
173 | ||
174 | /* | |
175 | * Check for urgent data... | |
176 | */ | |
177 | case DM: | |
178 | SYNCHing = stilloob(net); | |
179 | settimer(gotDM); | |
180 | break; | |
181 | ||
182 | ||
183 | /* | |
184 | * Begin option subnegotiation... | |
185 | */ | |
186 | case SB: | |
187 | state = TS_SB; | |
188 | SB_CLEAR(); | |
189 | continue; | |
190 | ||
191 | case WILL: | |
192 | state = TS_WILL; | |
193 | continue; | |
194 | ||
195 | case WONT: | |
196 | state = TS_WONT; | |
197 | continue; | |
198 | ||
199 | case DO: | |
200 | state = TS_DO; | |
201 | continue; | |
202 | ||
203 | case DONT: | |
204 | state = TS_DONT; | |
205 | continue; | |
206 | case EOR: | |
207 | if (hisopts[TELOPT_EOR]) | |
208 | doeof(); | |
209 | break; | |
210 | ||
211 | /* | |
212 | * Handle RFC 10xx Telnet linemode option additions | |
213 | * to command stream (EOF, SUSP, ABORT). | |
214 | */ | |
215 | case xEOF: | |
216 | doeof(); | |
217 | break; | |
218 | ||
219 | case SUSP: | |
220 | sendsusp(); | |
221 | break; | |
222 | ||
223 | case ABORT: | |
224 | sendbrk(); | |
225 | break; | |
226 | ||
227 | case IAC: | |
228 | *pfrontp++ = c; | |
229 | break; | |
230 | } | |
231 | state = TS_DATA; | |
232 | break; | |
233 | ||
234 | case TS_SB: | |
235 | if (c == IAC) { | |
236 | state = TS_SE; | |
237 | } else { | |
238 | SB_ACCUM(c); | |
239 | } | |
240 | break; | |
241 | ||
242 | case TS_SE: | |
243 | if (c != SE) { | |
244 | if (c != IAC) { | |
245 | /* | |
246 | * bad form of suboption negotiation. | |
247 | * handle it in such a way as to avoid | |
248 | * damage to local state. Parse | |
249 | * suboption buffer found so far, | |
250 | * then treat remaining stream as | |
251 | * another command sequence. | |
252 | */ | |
253 | SB_TERM(); | |
254 | suboption(); | |
255 | state = TS_IAC; | |
256 | goto gotiac; | |
257 | } | |
258 | SB_ACCUM(c); | |
259 | state = TS_SB; | |
260 | } else { | |
261 | SB_TERM(); | |
262 | suboption(); /* handle sub-option */ | |
263 | state = TS_DATA; | |
264 | } | |
265 | break; | |
266 | ||
267 | case TS_WILL: | |
268 | willoption(c, 0); | |
269 | state = TS_DATA; | |
270 | continue; | |
271 | ||
272 | case TS_WONT: | |
273 | wontoption(c, 0); | |
274 | state = TS_DATA; | |
275 | continue; | |
276 | ||
277 | case TS_DO: | |
278 | dooption(c, 0); | |
279 | state = TS_DATA; | |
280 | continue; | |
281 | ||
282 | case TS_DONT: | |
283 | dontoption(c, 0); | |
284 | state = TS_DATA; | |
285 | continue; | |
286 | ||
287 | default: | |
288 | syslog(LOG_ERR, "telnetd: panic state=%d\n", state); | |
289 | printf("telnetd: panic state=%d\n", state); | |
290 | exit(1); | |
291 | } | |
292 | } | |
293 | #if CRAY2 | |
294 | if (!linemode) { | |
295 | char xptyobuf[BUFSIZ+NETSLOP]; | |
296 | char xbuf2[BUFSIZ]; | |
297 | register char *cp; | |
298 | int n = pfrontp - opfrontp, oc; | |
299 | bcopy(opfrontp, xptyobuf, n); | |
300 | pfrontp = opfrontp; | |
301 | pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, | |
302 | xbuf2, &oc, BUFSIZ); | |
303 | for (cp = xbuf2; oc > 0; --oc) | |
304 | if ((*nfrontp++ = *cp++) == IAC) | |
305 | *nfrontp++ = IAC; | |
306 | } | |
307 | #endif /* CRAY2 */ | |
308 | } /* end of telrcv */ | |
309 | ||
310 | /* | |
311 | * The will/wont/do/dont state machines are based on Dave Borman's | |
312 | * Telnet option processing state machine. We keep track of the full | |
313 | * state of the option negotiation with the following state variables | |
314 | * myopts, hisopts - The last fully negotiated state for each | |
315 | * side of the connection. | |
316 | * mywants, hiswants - The state we wish to be in after a completed | |
317 | * negotiation. (hiswants is slightly misleading, | |
318 | * this is more precisely the state I want him to | |
319 | * be in. | |
320 | * resp - We count the number of requests we have sent out. | |
321 | * | |
322 | * These correspond to the following states: | |
323 | * my_state = the last negotiated state | |
324 | * want_state = what I want the state to go to | |
325 | * want_resp = how many requests I have sent | |
326 | * All state defaults are negative, and resp defaults to 0. | |
327 | * | |
328 | * When initiating a request to change state to new_state: | |
329 | * | |
330 | * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { | |
331 | * do nothing; | |
332 | * } else { | |
333 | * want_state = new_state; | |
334 | * send new_state; | |
335 | * want_resp++; | |
336 | * } | |
337 | * | |
338 | * When receiving new_state: | |
339 | * | |
340 | * if (want_resp) { | |
341 | * want_resp--; | |
342 | * if (want_resp && (new_state == my_state)) | |
343 | * want_resp--; | |
344 | * } | |
345 | * if ((want_resp == 0) && (new_state != want_state)) { | |
346 | * if (ok_to_switch_to new_state) | |
347 | * want_state = new_state; | |
348 | * else | |
349 | * want_resp++; | |
350 | * send want_state; | |
351 | * } | |
352 | * my_state = new_state; | |
353 | * | |
354 | * Note that new_state is implied in these functions by the function itself. | |
355 | * will and do imply positive new_state, wont and dont imply negative. | |
356 | * | |
357 | * Finally, there is one catch. If we send a negative response to a | |
358 | * positive request, my_state will be the positive while want_state will | |
359 | * remain negative. my_state will revert to negative when the negative | |
360 | * acknowlegment arrives from the peer. Thus, my_state generally tells | |
361 | * us not only the last negotiated state, but also tells us what the peer | |
362 | * wants to be doing as well. It is important to understand this difference | |
363 | * as we may wish to be processing data streams based on our desired state | |
364 | * (want_state) or based on what the peer thinks the state is (my_state). | |
365 | * | |
366 | * This all works fine because if the peer sends a positive request, the data | |
367 | * that we receive prior to negative acknowlegment will probably be affected | |
368 | * by the positive state, and we can process it as such (if we can; if we | |
369 | * can't then it really doesn't matter). If it is that important, then the | |
370 | * peer probably should be buffering until this option state negotiation | |
371 | * is complete. | |
372 | * | |
373 | * In processing options, request signifies whether this is a request | |
374 | * to send or a response. request is true if this is a request to | |
375 | * send generated locally. | |
376 | */ | |
377 | willoption(option, request) | |
378 | int option, request; | |
379 | { | |
380 | int changeok = 0; | |
381 | char *fmt = (char *)0; | |
382 | ||
383 | /* | |
384 | * process input from peer. | |
385 | */ | |
386 | if (request == 0) { | |
387 | switch (option) { | |
388 | ||
389 | case TELOPT_BINARY: | |
390 | init_termbuf(); | |
391 | tty_binaryin(1); | |
392 | set_termbuf(); | |
393 | changeok++; | |
394 | break; | |
395 | ||
396 | case TELOPT_ECHO: | |
397 | not42 = 0; /* looks like a 4.2 system */ | |
398 | /* | |
399 | * Now, in a 4.2 system, to break them out of ECHOing | |
400 | * (to the terminal) mode, we need to send a | |
401 | * "WILL ECHO". Kludge upon kludge! | |
402 | */ | |
403 | if (myopts[TELOPT_ECHO] == OPT_YES) { | |
404 | dooption(TELOPT_ECHO, 1); | |
405 | } | |
406 | /* | |
407 | * Fool the state machine into sending a don't. | |
408 | * This also allows the initial echo sending code to | |
409 | * break out of the loop that it is | |
410 | * in. (Look in telnet()) | |
411 | */ | |
412 | hiswants[TELOPT_ECHO] = OPT_NO; | |
413 | break; | |
414 | ||
415 | case TELOPT_TM: | |
416 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
417 | /* | |
418 | * This telnetd implementation does not really support | |
419 | * timing marks, it just uses them to support the kludge | |
420 | * linemode stuff. If we receive a will or wont TM in | |
421 | * response to our do TM request that may have been sent | |
422 | * to determine kludge linemode support, process it, | |
423 | * otherwise TM should get a negative response back. | |
424 | */ | |
425 | /* | |
426 | * Handle the linemode kludge stuff. | |
427 | * If we are not currently supporting any | |
428 | * linemode at all, then we assume that this | |
429 | * is the client telling us to use kludge | |
430 | * linemode in response to our query. Set the | |
431 | * linemode type that is to be supported, note | |
432 | * that the client wishes to use linemode, and | |
433 | * eat the will TM as though it never arrived. | |
434 | */ | |
435 | if (lmodetype < KLUDGE_LINEMODE) { | |
436 | lmodetype = KLUDGE_LINEMODE; | |
437 | clientstat(TELOPT_LINEMODE, WILL, 0); | |
438 | dontoption(TELOPT_SGA, 0); | |
439 | } | |
440 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
441 | /* | |
442 | * cheat the state machine so that it | |
443 | * looks like we never sent the TM at | |
444 | * all. The bad part of this is that | |
445 | * if the client sends a will TM on his | |
446 | * own to turn on linemode, then he | |
447 | * won't get a response. | |
448 | */ | |
449 | hiswants[TELOPT_TM] = OPT_NO; | |
450 | resp[TELOPT_TM]--; | |
451 | return; | |
452 | ||
453 | case TELOPT_LFLOW: | |
454 | /* | |
455 | * If we are going to support flow control option, | |
456 | * then don't worry peer that we can't change the | |
457 | * flow control characters. | |
458 | */ | |
459 | slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; | |
460 | slctab[SLC_XON].defset.flag |= SLC_DEFAULT; | |
461 | slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; | |
462 | slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; | |
463 | case TELOPT_TTYPE: | |
464 | case TELOPT_SGA: | |
465 | case TELOPT_NAWS: | |
466 | case TELOPT_TSPEED: | |
467 | #ifdef LINEMODE | |
468 | case TELOPT_LINEMODE: | |
469 | #endif LINEMODE | |
470 | changeok++; | |
471 | break; | |
472 | ||
473 | default: | |
474 | break; | |
475 | } | |
476 | ||
477 | } | |
478 | ||
479 | if (request) { | |
480 | if (!((resp[option] == 0 && hisopts[option] == OPT_YES) || | |
481 | hiswants[option] == OPT_YES)) { | |
482 | hiswants[option] = OPT_YES; | |
483 | fmt = doopt; | |
484 | resp[option]++; | |
485 | } | |
486 | } else { | |
487 | if (resp[option]) { | |
488 | resp[option]--; | |
489 | if (resp[option] && hisopts[option] == OPT_YES) | |
490 | resp[option]--; | |
491 | } | |
492 | if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) { | |
493 | if (changeok) | |
494 | hiswants[option] = OPT_YES; | |
495 | else | |
496 | resp[option]++; | |
497 | fmt = (hiswants[option] ? doopt : dont); | |
498 | } | |
499 | hisopts[option] = OPT_YES; | |
500 | } | |
501 | ||
502 | if (fmt) { | |
503 | (void) sprintf(nfrontp, fmt, option); | |
504 | nfrontp += sizeof (dont) - 2; | |
505 | } | |
506 | ||
507 | /* | |
508 | * Handle other processing that should occur after we have | |
509 | * responded to client input. | |
510 | */ | |
511 | if (!request) { | |
512 | switch (option) { | |
513 | #ifdef LINEMODE | |
514 | case TELOPT_LINEMODE: | |
515 | # ifdef KLUDGELINEMODE | |
516 | /* | |
517 | * Note client's desire to use linemode. | |
518 | */ | |
519 | lmodetype = REAL_LINEMODE; | |
520 | # endif /* KLUDGELINEMODE */ | |
521 | clientstat(TELOPT_LINEMODE, WILL, 0); | |
522 | break; | |
523 | #endif LINEMODE | |
524 | ||
525 | default: | |
526 | break; | |
527 | } | |
528 | } | |
529 | ||
530 | } /* end of willoption */ | |
531 | ||
532 | wontoption(option, request) | |
533 | int option, request; | |
534 | { | |
535 | char *fmt = (char *)0; | |
536 | ||
537 | /* | |
538 | * Process client input. | |
539 | */ | |
540 | if (!request) { | |
541 | switch (option) { | |
542 | case TELOPT_ECHO: | |
543 | not42 = 1; /* doesn't seem to be a 4.2 system */ | |
544 | break; | |
545 | ||
546 | case TELOPT_BINARY: | |
547 | init_termbuf(); | |
548 | tty_binaryin(0); | |
549 | set_termbuf(); | |
550 | break; | |
551 | ||
552 | #ifdef LINEMODE | |
553 | case TELOPT_LINEMODE: | |
554 | # ifdef KLUDGELINEMODE | |
555 | /* | |
556 | * If real linemode is supported, then client is | |
557 | * asking to turn linemode off. | |
558 | */ | |
559 | if (lmodetype == REAL_LINEMODE) | |
560 | # endif /* KLUDGELINEMODE */ | |
561 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
562 | break; | |
563 | #endif LINEMODE | |
564 | ||
565 | case TELOPT_TM: | |
566 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
567 | if (lmodetype < REAL_LINEMODE) { | |
568 | lmodetype = NO_LINEMODE; | |
569 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
570 | dooption(TELOPT_SGA, 0); | |
571 | } | |
572 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
573 | /* | |
574 | * If we get a WONT TM, and had sent a DO TM, don't | |
575 | * respond with a DONT TM, just leave it as is. | |
576 | * Short circut the state machine to achive this. | |
577 | * The bad part of this is that if the client sends | |
578 | * a WONT TM on his own to turn off linemode, then he | |
579 | * won't get a response. | |
580 | */ | |
581 | hiswants[TELOPT_TM] = OPT_NO; | |
582 | resp[TELOPT_TM]--; | |
583 | return; | |
584 | ||
585 | case TELOPT_LFLOW: | |
586 | /* | |
587 | * If we are not going to support flow control option, | |
588 | * then let peer know that we can't change the | |
589 | * flow control characters. | |
590 | */ | |
591 | slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; | |
592 | slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; | |
593 | slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; | |
594 | slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; | |
595 | break; | |
596 | ||
597 | default: | |
598 | break; | |
599 | } | |
600 | } | |
601 | ||
602 | ||
603 | if (request) { | |
604 | if (!((resp[option] == 0 && hisopts[option] == OPT_NO) || | |
605 | hiswants[option] == OPT_NO)) { | |
606 | hiswants[option] = OPT_NO; | |
607 | fmt = dont; | |
608 | resp[option]++; | |
609 | } | |
610 | } else { | |
611 | if (resp[option]) { | |
612 | resp[option]--; | |
613 | if (resp[option] && hisopts[option] == OPT_NO) | |
614 | resp[option]--; | |
615 | } | |
616 | if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) { | |
617 | /* it is always ok to change to negative state */ | |
618 | hiswants[option] = OPT_NO; | |
619 | fmt = dont; | |
620 | } | |
621 | hisopts[option] = OPT_NO; | |
622 | } | |
623 | ||
624 | if (fmt) { | |
625 | (void) sprintf(nfrontp, fmt, option); | |
626 | nfrontp += sizeof (doopt) - 2; | |
627 | } | |
628 | ||
629 | } /* end of wontoption */ | |
630 | ||
631 | dooption(option, request) | |
632 | int option, request; | |
633 | { | |
634 | int changeok = 0; | |
635 | char *fmt = (char *)0; | |
636 | ||
637 | /* | |
638 | * Process client input. | |
639 | */ | |
640 | if (!request) { | |
641 | switch (option) { | |
642 | case TELOPT_ECHO: | |
643 | #ifdef LINEMODE | |
644 | if (hisopts[TELOPT_LINEMODE] == OPT_NO) { | |
645 | #endif | |
646 | init_termbuf(); | |
647 | tty_setecho(1); | |
648 | set_termbuf(); | |
649 | #ifdef LINEMODE | |
650 | } | |
651 | #endif | |
652 | changeok++; | |
653 | break; | |
654 | ||
655 | case TELOPT_BINARY: | |
656 | init_termbuf(); | |
657 | tty_binaryout(1); | |
658 | set_termbuf(); | |
659 | changeok++; | |
660 | break; | |
661 | ||
662 | case TELOPT_SGA: | |
663 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
664 | /* | |
665 | * If kludge linemode is in use, then we must process | |
666 | * an incoming do SGA for linemode purposes. | |
667 | */ | |
668 | if (lmodetype == KLUDGE_LINEMODE) { | |
669 | /* | |
670 | * Receipt of "do SGA" in kludge linemode | |
671 | * is the peer asking us to turn off linemode. | |
672 | * Make note of the request. | |
673 | */ | |
674 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
675 | /* | |
676 | * If linemode did not get turned off then | |
677 | * don't tell peer that we did. Breaking | |
678 | * here forces a wont SGA to be returned. | |
679 | */ | |
680 | if (linemode) | |
681 | break; | |
682 | } | |
683 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
684 | changeok++; | |
685 | break; | |
686 | ||
687 | case TELOPT_STATUS: | |
688 | changeok++; | |
689 | break; | |
690 | ||
691 | case TELOPT_TM: | |
692 | case TELOPT_LINEMODE: | |
693 | case TELOPT_TTYPE: | |
694 | case TELOPT_NAWS: | |
695 | case TELOPT_TSPEED: | |
696 | case TELOPT_LFLOW: | |
697 | default: | |
698 | break; | |
699 | } | |
700 | } | |
701 | ||
702 | if (request) { | |
703 | if (!((resp[option] == 0 && myopts[option] == OPT_YES) || | |
704 | mywants[option] == OPT_YES)) { | |
705 | mywants[option] = OPT_YES; | |
706 | fmt = will; | |
707 | resp[option]++; | |
708 | } | |
709 | } else { | |
710 | if (resp[option]) { | |
711 | resp[option]--; | |
712 | if (resp[option] && myopts[option] == OPT_YES) | |
713 | resp[option]--; | |
714 | } | |
715 | if ((resp[option] == 0) && (mywants[option] != OPT_YES)) { | |
716 | if (changeok) | |
717 | mywants[option] = OPT_YES; | |
718 | else | |
719 | resp[option]++; | |
720 | fmt = (mywants[option] ? will : wont); | |
721 | } | |
722 | myopts[option] = OPT_YES; | |
723 | } | |
724 | ||
725 | if (fmt) { | |
726 | (void) sprintf(nfrontp, fmt, option); | |
727 | nfrontp += sizeof (doopt) - 2; | |
728 | } | |
729 | ||
730 | } /* end of dooption */ | |
731 | ||
732 | ||
733 | dontoption(option, request) | |
734 | int option, request; | |
735 | { | |
736 | char *fmt = (char *)0; | |
737 | ||
738 | /* | |
739 | * Process client input. | |
740 | */ | |
741 | if (!request) { | |
742 | switch (option) { | |
743 | case TELOPT_BINARY: | |
744 | init_termbuf(); | |
745 | tty_binaryout(0); | |
746 | set_termbuf(); | |
747 | break; | |
748 | ||
749 | case TELOPT_ECHO: /* we should stop echoing */ | |
750 | #ifdef LINEMODE | |
751 | if (hisopts[TELOPT_LINEMODE] == OPT_NO) { | |
752 | #endif | |
753 | init_termbuf(); | |
754 | tty_setecho(0); | |
755 | set_termbuf(); | |
756 | #ifdef LINEMODE | |
757 | } | |
758 | #endif | |
759 | break; | |
760 | ||
761 | case TELOPT_SGA: | |
762 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
763 | /* | |
764 | * If kludge linemode is in use, then we must process an | |
765 | * incoming do SGA for linemode purposes. | |
766 | */ | |
767 | if (lmodetype == KLUDGE_LINEMODE) { | |
768 | /* | |
769 | * The client is asking us to turn linemode | |
770 | * on. | |
771 | */ | |
772 | clientstat(TELOPT_LINEMODE, WILL, 0); | |
773 | /* | |
774 | * If we did not turn line mode on, then what do | |
775 | * we say? Will SGA? This violates design of | |
776 | * telnet. Gross. Very Gross. | |
777 | */ | |
778 | } | |
779 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
780 | ||
781 | default: | |
782 | break; | |
783 | } | |
784 | } | |
785 | ||
786 | if (request) { | |
787 | if (!((resp[option] == 0 && myopts[option] == OPT_NO) || | |
788 | mywants[option] == OPT_NO)) { | |
789 | mywants[option] = OPT_NO; | |
790 | fmt = wont; | |
791 | resp[option]++; | |
792 | } | |
793 | } else { | |
794 | if (resp[option]) { | |
795 | resp[option]--; | |
796 | if (resp[option] && myopts[option] == OPT_NO) | |
797 | resp[option]--; | |
798 | } | |
799 | if ((resp[option] == 0) && (mywants[option] != OPT_NO)) { | |
800 | mywants[option] = OPT_NO; | |
801 | fmt = wont; | |
802 | } | |
803 | myopts[option] = OPT_NO; | |
804 | } | |
805 | ||
806 | if (fmt) { | |
807 | (void) sprintf(nfrontp, fmt, option); | |
808 | nfrontp += sizeof (wont) - 2; | |
809 | } | |
810 | ||
811 | } /* end of dontoption */ | |
812 | ||
813 | /* | |
814 | * suboption() | |
815 | * | |
816 | * Look at the sub-option buffer, and try to be helpful to the other | |
817 | * side. | |
818 | * | |
819 | * Currently we recognize: | |
820 | * | |
821 | * Terminal type is | |
822 | * Linemode | |
823 | * Window size | |
824 | * Terminal speed | |
825 | */ | |
826 | suboption() | |
827 | { | |
828 | register int subchar; | |
829 | ||
830 | subchar = SB_GET(); | |
831 | switch (subchar) { | |
832 | case TELOPT_TSPEED: { | |
833 | register int xspeed, rspeed; | |
834 | ||
835 | if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ | |
836 | break; | |
837 | ||
838 | settimer(tspeedsubopt); | |
839 | ||
840 | if (SB_EOF() || SB_GET() != TELQUAL_IS) | |
841 | return; | |
842 | ||
843 | xspeed = atoi(subpointer); | |
844 | ||
845 | while (SB_GET() != ',' && !SB_EOF()); | |
846 | if (SB_EOF()) | |
847 | return; | |
848 | ||
849 | rspeed = atoi(subpointer); | |
850 | clientstat(TELOPT_TSPEED, xspeed, rspeed); | |
851 | ||
852 | break; | |
853 | ||
854 | } /* end of case TELOPT_TSPEED */ | |
855 | ||
856 | case TELOPT_TTYPE: { /* Yaaaay! */ | |
857 | static char terminalname[5+41] = "TERM="; | |
858 | ||
859 | if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ | |
860 | break; | |
861 | settimer(ttypesubopt); | |
862 | ||
863 | if (SB_GET() != TELQUAL_IS) { | |
864 | return; /* ??? XXX but, this is the most robust */ | |
865 | } | |
866 | ||
867 | terminaltype = terminalname+sizeof("TERM=")-1; | |
868 | ||
869 | while ((terminaltype < (terminalname + sizeof terminalname-1)) && | |
870 | !SB_EOF()) { | |
871 | register int c; | |
872 | ||
873 | c = SB_GET(); | |
874 | if (isupper(c)) { | |
875 | c = tolower(c); | |
876 | } | |
877 | *terminaltype++ = c; /* accumulate name */ | |
878 | } | |
879 | *terminaltype = 0; | |
880 | terminaltype = terminalname; | |
881 | break; | |
882 | } /* end of case TELOPT_TTYPE */ | |
883 | ||
884 | case TELOPT_NAWS: { | |
885 | register int xwinsize, ywinsize; | |
886 | ||
887 | if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ | |
888 | break; | |
889 | ||
890 | if (SB_EOF()) | |
891 | return; | |
892 | xwinsize = SB_GET() << 8; | |
893 | if (SB_EOF()) | |
894 | return; | |
895 | xwinsize |= SB_GET(); | |
896 | if (SB_EOF()) | |
897 | return; | |
898 | ywinsize = SB_GET() << 8; | |
899 | if (SB_EOF()) | |
900 | return; | |
901 | ywinsize |= SB_GET(); | |
902 | clientstat(TELOPT_NAWS, xwinsize, ywinsize); | |
903 | ||
904 | break; | |
905 | ||
906 | } /* end of case TELOPT_NAWS */ | |
907 | ||
908 | #ifdef LINEMODE | |
909 | case TELOPT_LINEMODE: { | |
910 | register int request; | |
911 | ||
912 | if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ | |
913 | break; | |
914 | /* | |
915 | * Process linemode suboptions. | |
916 | */ | |
917 | if (SB_EOF()) break; /* garbage was sent */ | |
918 | request = SB_GET(); /* get will/wont */ | |
919 | if (SB_EOF()) break; /* another garbage check */ | |
920 | ||
921 | if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ | |
922 | /* | |
923 | * Process suboption buffer of slc's | |
924 | */ | |
925 | start_slc(1); | |
926 | do_opt_slc(subpointer, subend - subpointer); | |
927 | end_slc(0); | |
928 | ||
929 | } else if (request == LM_MODE) { | |
930 | useeditmode = SB_GET(); /* get mode flag */ | |
931 | clientstat(LM_MODE, 0, 0); | |
932 | } | |
933 | ||
934 | switch (SB_GET()) { /* what suboption? */ | |
935 | case LM_FORWARDMASK: | |
936 | /* | |
937 | * According to spec, only server can send request for | |
938 | * forwardmask, and client can only return a positive response. | |
939 | * So don't worry about it. | |
940 | */ | |
941 | ||
942 | default: | |
943 | break; | |
944 | } | |
945 | ||
946 | } /* end of case TELOPT_LINEMODE */ | |
947 | #endif | |
948 | case TELOPT_STATUS: { | |
949 | int mode; | |
950 | ||
951 | mode = SB_GET(); | |
952 | switch (mode) { | |
953 | case TELQUAL_SEND: | |
954 | if (myopts[TELOPT_STATUS] == OPT_YES) | |
955 | send_status(); | |
956 | break; | |
957 | ||
958 | case TELQUAL_IS: | |
959 | break; | |
960 | ||
961 | default: | |
962 | break; | |
963 | } | |
964 | } | |
965 | ||
966 | default: | |
967 | break; | |
968 | } /* end of switch */ | |
969 | ||
970 | } /* end of suboption */ | |
971 | ||
972 | #define ADD(c) *ncp++ = c; | |
973 | #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } | |
974 | send_status() | |
975 | { | |
976 | char statusbuf[256]; | |
977 | register char *ncp; | |
978 | register int i; | |
979 | ||
980 | ncp = statusbuf; | |
981 | ||
982 | netflush(); /* get rid of anything waiting to go out */ | |
983 | ||
984 | ADD(IAC); | |
985 | ADD(SB); | |
986 | ADD(TELOPT_STATUS); | |
987 | ADD(TELQUAL_IS); | |
988 | ||
989 | for (i = 0; i < NTELOPTS; i++) { | |
990 | if (myopts[i] == OPT_YES) { | |
991 | ADD(WILL); | |
992 | ADD_DATA(i); | |
993 | if (i == IAC) | |
994 | ADD(IAC); | |
995 | } | |
996 | if (hisopts[i] == OPT_YES) { | |
997 | ADD(DO); | |
998 | ADD_DATA(i); | |
999 | if (i == IAC) | |
1000 | ADD(IAC); | |
1001 | } | |
1002 | } | |
1003 | ||
1004 | #ifdef LINEMODE | |
1005 | if (hisopts[TELOPT_LINEMODE] == OPT_YES) { | |
1006 | char *cp, *cpe; | |
1007 | int len; | |
1008 | ||
1009 | ADD(SB); | |
1010 | ADD(TELOPT_LINEMODE); | |
1011 | ADD(LM_MODE); | |
1012 | ADD_DATA(editmode); | |
1013 | if (editmode == IAC) | |
1014 | ADD(IAC); | |
1015 | ADD(SE); | |
1016 | ||
1017 | ADD(SB); | |
1018 | ADD(TELOPT_LINEMODE); | |
1019 | ADD(LM_SLC); | |
1020 | start_slc(0); | |
1021 | send_slc(); | |
1022 | len = end_slc(&cp); | |
1023 | for (cpe = cp + len; cp < cpe; cp++) | |
1024 | ADD_DATA(*cp); | |
1025 | ADD(SE); | |
1026 | } | |
1027 | #endif /* LINEMODE */ | |
1028 | ||
1029 | ADD(IAC); | |
1030 | ADD(SE); | |
1031 | ||
1032 | writenet(statusbuf, ncp - statusbuf); | |
1033 | netflush(); /* Send it on its way */ | |
1034 | } |