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 | |
053fd49d | 19 | static char sccsid[] = "@(#)state.c 5.3 (Berkeley) %G%"; |
ea139302 PB |
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: | |
053fd49d | 268 | willoption(c); |
ea139302 PB |
269 | state = TS_DATA; |
270 | continue; | |
271 | ||
272 | case TS_WONT: | |
053fd49d | 273 | wontoption(c); |
ea139302 PB |
274 | state = TS_DATA; |
275 | continue; | |
276 | ||
277 | case TS_DO: | |
053fd49d | 278 | dooption(c); |
ea139302 PB |
279 | state = TS_DATA; |
280 | continue; | |
281 | ||
282 | case TS_DONT: | |
053fd49d | 283 | dontoption(c); |
ea139302 PB |
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 | */ | |
053fd49d PB |
377 | send_do(option, init) |
378 | int option, init; | |
379 | { | |
380 | if (init) { | |
381 | if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) || | |
382 | hiswants[option] == OPT_YES) | |
383 | return; | |
384 | hiswants[option] = OPT_YES; | |
385 | do_dont_resp[option]++; | |
386 | } | |
387 | (void) sprintf(nfrontp, doopt, option); | |
388 | nfrontp += sizeof (dont) - 2; | |
389 | } | |
390 | ||
391 | willoption(option) | |
392 | int option; | |
ea139302 PB |
393 | { |
394 | int changeok = 0; | |
ea139302 | 395 | |
053fd49d PB |
396 | /* |
397 | * process input from peer. | |
398 | */ | |
399 | ||
400 | if (do_dont_resp[option]) { | |
401 | do_dont_resp[option]--; | |
402 | if (do_dont_resp[option] && hisopts[option] == OPT_YES) | |
403 | do_dont_resp[option]--; | |
404 | } | |
405 | if ((do_dont_resp[option] == 0) && (hiswants[option] != OPT_YES)) { | |
ea139302 PB |
406 | switch (option) { |
407 | ||
408 | case TELOPT_BINARY: | |
409 | init_termbuf(); | |
410 | tty_binaryin(1); | |
411 | set_termbuf(); | |
412 | changeok++; | |
413 | break; | |
414 | ||
415 | case TELOPT_ECHO: | |
053fd49d PB |
416 | not42 = 0; /* looks like a 4.2 system */ |
417 | #ifdef notdef | |
ea139302 | 418 | /* |
053fd49d PB |
419 | * Now, in a 4.2 system, to break them out of |
420 | * ECHOing (to the terminal) mode, we need to | |
421 | * send a WILL ECHO. | |
ea139302 PB |
422 | */ |
423 | if (myopts[TELOPT_ECHO] == OPT_YES) { | |
053fd49d | 424 | send_will(TELOPT_ECHO, 1); |
ea139302 | 425 | } |
053fd49d PB |
426 | #else |
427 | /* | |
428 | * "WILL ECHO". Kludge upon kludge! | |
429 | * A 4.2 client is now echoing user input at | |
430 | * the tty. This is probably undesireable and | |
431 | * it should be stopped. The client will | |
432 | * respond WONT TM to the DO TM that we send to | |
433 | * check for kludge linemode. When the WONT TM | |
434 | * arrives, linemode will be turned off and a | |
435 | * change propogated to the pty. This change | |
436 | * will cause us to process the new pty state | |
437 | * in localstat(), which will notice that | |
438 | * linemode is off and send a WILL ECHO | |
439 | * so that we are properly in character mode and | |
440 | * all is well. | |
441 | */ | |
442 | #endif | |
ea139302 PB |
443 | /* |
444 | * Fool the state machine into sending a don't. | |
053fd49d PB |
445 | * This also allows the initial echo sending |
446 | * code to break out of the loop that it is | |
ea139302 PB |
447 | * in. (Look in telnet()) |
448 | */ | |
449 | hiswants[TELOPT_ECHO] = OPT_NO; | |
450 | break; | |
451 | ||
452 | case TELOPT_TM: | |
453 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
454 | /* | |
053fd49d PB |
455 | * This telnetd implementation does not really |
456 | * support timing marks, it just uses them to | |
457 | * support the kludge linemode stuff. If we | |
458 | * receive a will or wont TM in response to our | |
459 | * do TM request that may have been sent to | |
460 | * determine kludge linemode support, process | |
461 | * it, otherwise TM should get a negative | |
462 | * response back. | |
ea139302 PB |
463 | */ |
464 | /* | |
465 | * Handle the linemode kludge stuff. | |
466 | * If we are not currently supporting any | |
467 | * linemode at all, then we assume that this | |
468 | * is the client telling us to use kludge | |
469 | * linemode in response to our query. Set the | |
470 | * linemode type that is to be supported, note | |
471 | * that the client wishes to use linemode, and | |
472 | * eat the will TM as though it never arrived. | |
473 | */ | |
474 | if (lmodetype < KLUDGE_LINEMODE) { | |
475 | lmodetype = KLUDGE_LINEMODE; | |
476 | clientstat(TELOPT_LINEMODE, WILL, 0); | |
053fd49d | 477 | send_wont(TELOPT_SGA, 1); |
ea139302 PB |
478 | } |
479 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
480 | /* | |
053fd49d PB |
481 | * Cheat the state machine so that it |
482 | * looks like we got back a WONT. | |
ea139302 PB |
483 | */ |
484 | hiswants[TELOPT_TM] = OPT_NO; | |
ea139302 PB |
485 | return; |
486 | ||
487 | case TELOPT_LFLOW: | |
488 | /* | |
053fd49d PB |
489 | * If we are going to support flow control |
490 | * option, then don't worry peer that we can't | |
491 | * change the flow control characters. | |
ea139302 PB |
492 | */ |
493 | slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; | |
494 | slctab[SLC_XON].defset.flag |= SLC_DEFAULT; | |
495 | slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; | |
496 | slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; | |
497 | case TELOPT_TTYPE: | |
498 | case TELOPT_SGA: | |
499 | case TELOPT_NAWS: | |
500 | case TELOPT_TSPEED: | |
501 | #ifdef LINEMODE | |
502 | case TELOPT_LINEMODE: | |
503 | #endif LINEMODE | |
504 | changeok++; | |
505 | break; | |
506 | ||
507 | default: | |
508 | break; | |
509 | } | |
053fd49d | 510 | if (changeok) { |
ea139302 | 511 | hiswants[option] = OPT_YES; |
053fd49d PB |
512 | send_do(option, 0); |
513 | } else { | |
514 | do_dont_resp[option]++; | |
515 | send_dont(option, 0); | |
ea139302 | 516 | } |
ea139302 | 517 | } |
053fd49d | 518 | hisopts[option] = OPT_YES; |
ea139302 PB |
519 | |
520 | /* | |
521 | * Handle other processing that should occur after we have | |
522 | * responded to client input. | |
523 | */ | |
053fd49d | 524 | switch (option) { |
ea139302 | 525 | #ifdef LINEMODE |
053fd49d | 526 | case TELOPT_LINEMODE: |
ea139302 | 527 | # ifdef KLUDGELINEMODE |
053fd49d PB |
528 | /* |
529 | * Note client's desire to use linemode. | |
530 | */ | |
531 | lmodetype = REAL_LINEMODE; | |
ea139302 | 532 | # endif /* KLUDGELINEMODE */ |
053fd49d PB |
533 | clientstat(TELOPT_LINEMODE, WILL, 0); |
534 | break; | |
ea139302 | 535 | #endif LINEMODE |
053fd49d PB |
536 | |
537 | default: | |
538 | break; | |
ea139302 | 539 | } |
ea139302 PB |
540 | } /* end of willoption */ |
541 | ||
053fd49d PB |
542 | send_dont(option, init) |
543 | int option, init; | |
544 | { | |
545 | if (init) { | |
546 | if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) || | |
547 | hiswants[option] == OPT_NO) | |
548 | return; | |
549 | hiswants[option] = OPT_NO; | |
550 | do_dont_resp[option]++; | |
551 | } | |
552 | (void) sprintf(nfrontp, dont, option); | |
553 | nfrontp += sizeof (doopt) - 2; | |
554 | } | |
555 | ||
556 | wontoption(option) | |
557 | int option; | |
ea139302 PB |
558 | { |
559 | char *fmt = (char *)0; | |
560 | ||
561 | /* | |
562 | * Process client input. | |
563 | */ | |
053fd49d PB |
564 | |
565 | if (do_dont_resp[option]) { | |
566 | do_dont_resp[option]--; | |
567 | if (do_dont_resp[option] && hisopts[option] == OPT_NO) | |
568 | do_dont_resp[option]--; | |
569 | } | |
570 | if ((do_dont_resp[option] == 0) && (hiswants[option] != OPT_NO)) { | |
571 | /* it is always ok to change to negative state */ | |
ea139302 PB |
572 | switch (option) { |
573 | case TELOPT_ECHO: | |
053fd49d | 574 | not42 = 1; /* doesn't seem to be a 4.2 system */ |
ea139302 PB |
575 | break; |
576 | ||
577 | case TELOPT_BINARY: | |
578 | init_termbuf(); | |
579 | tty_binaryin(0); | |
580 | set_termbuf(); | |
581 | break; | |
582 | ||
583 | #ifdef LINEMODE | |
584 | case TELOPT_LINEMODE: | |
585 | # ifdef KLUDGELINEMODE | |
586 | /* | |
587 | * If real linemode is supported, then client is | |
588 | * asking to turn linemode off. | |
589 | */ | |
590 | if (lmodetype == REAL_LINEMODE) | |
591 | # endif /* KLUDGELINEMODE */ | |
592 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
593 | break; | |
594 | #endif LINEMODE | |
595 | ||
596 | case TELOPT_TM: | |
597 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
598 | if (lmodetype < REAL_LINEMODE) { | |
599 | lmodetype = NO_LINEMODE; | |
600 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
053fd49d PB |
601 | send_will(TELOPT_SGA, 1); |
602 | /*@*/ send_will(TELOPT_ECHO, 1); | |
ea139302 PB |
603 | } |
604 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
605 | /* | |
053fd49d PB |
606 | * If we get a WONT TM, and had sent a DO TM, |
607 | * don't respond with a DONT TM, just leave it | |
608 | * as is. Short circut the state machine to | |
609 | * achive this. The bad part of this is that if | |
610 | * the client sends a WONT TM on his own to | |
611 | * turn off linemode, then he won't get a | |
612 | * response. | |
ea139302 PB |
613 | */ |
614 | hiswants[TELOPT_TM] = OPT_NO; | |
053fd49d PB |
615 | #ifdef notdef |
616 | do_dont_resp[TELOPT_TM]--; | |
617 | #endif | |
ea139302 PB |
618 | return; |
619 | ||
620 | case TELOPT_LFLOW: | |
621 | /* | |
053fd49d PB |
622 | * If we are not going to support flow control |
623 | * option, then let peer know that we can't | |
624 | * change the flow control characters. | |
ea139302 PB |
625 | */ |
626 | slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; | |
627 | slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; | |
628 | slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; | |
629 | slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; | |
630 | break; | |
631 | ||
632 | default: | |
633 | break; | |
634 | } | |
053fd49d PB |
635 | hiswants[option] = OPT_NO; |
636 | fmt = dont; | |
637 | send_dont(option, 0); | |
638 | } else if (option == TELOPT_TM) { | |
639 | /* | |
640 | * Special case for TM. | |
641 | */ | |
642 | hiswants[option] = OPT_NO; | |
ea139302 | 643 | } |
053fd49d | 644 | hisopts[option] = OPT_NO; |
ea139302 | 645 | |
053fd49d | 646 | } /* end of wontoption */ |
ea139302 | 647 | |
053fd49d PB |
648 | send_will(option, init) |
649 | int option, init; | |
650 | { | |
651 | if (init) { | |
652 | if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)|| | |
653 | mywants[option] == OPT_YES) | |
654 | return; | |
655 | mywants[option] = OPT_YES; | |
656 | will_wont_resp[option]++; | |
ea139302 | 657 | } |
053fd49d PB |
658 | (void) sprintf(nfrontp, will, option); |
659 | nfrontp += sizeof (doopt) - 2; | |
660 | } | |
ea139302 | 661 | |
053fd49d PB |
662 | dooption(option) |
663 | int option; | |
ea139302 PB |
664 | { |
665 | int changeok = 0; | |
ea139302 PB |
666 | |
667 | /* | |
668 | * Process client input. | |
669 | */ | |
053fd49d PB |
670 | |
671 | if (will_wont_resp[option]) { | |
672 | will_wont_resp[option]--; | |
673 | if (will_wont_resp[option] && myopts[option] == OPT_YES) | |
674 | will_wont_resp[option]--; | |
675 | } | |
676 | if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) { | |
ea139302 PB |
677 | switch (option) { |
678 | case TELOPT_ECHO: | |
679 | #ifdef LINEMODE | |
053fd49d | 680 | if (lmodetype == NO_LINEMODE) { |
ea139302 PB |
681 | #endif |
682 | init_termbuf(); | |
683 | tty_setecho(1); | |
684 | set_termbuf(); | |
685 | #ifdef LINEMODE | |
686 | } | |
687 | #endif | |
688 | changeok++; | |
689 | break; | |
690 | ||
691 | case TELOPT_BINARY: | |
692 | init_termbuf(); | |
693 | tty_binaryout(1); | |
694 | set_termbuf(); | |
695 | changeok++; | |
696 | break; | |
697 | ||
698 | case TELOPT_SGA: | |
699 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
700 | /* | |
053fd49d PB |
701 | * If kludge linemode is in use, then we must |
702 | * process an incoming do SGA for linemode | |
703 | * purposes. | |
ea139302 PB |
704 | */ |
705 | if (lmodetype == KLUDGE_LINEMODE) { | |
706 | /* | |
053fd49d PB |
707 | * Receipt of "do SGA" in kludge |
708 | * linemode is the peer asking us to | |
709 | * turn off linemode. Make note of | |
710 | * the request. | |
ea139302 PB |
711 | */ |
712 | clientstat(TELOPT_LINEMODE, WONT, 0); | |
713 | /* | |
053fd49d PB |
714 | * If linemode did not get turned off |
715 | * then don't tell peer that we did. | |
716 | * Breaking here forces a wont SGA to | |
717 | * be returned. | |
ea139302 PB |
718 | */ |
719 | if (linemode) | |
720 | break; | |
721 | } | |
722 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
723 | changeok++; | |
724 | break; | |
725 | ||
726 | case TELOPT_STATUS: | |
727 | changeok++; | |
728 | break; | |
729 | ||
730 | case TELOPT_TM: | |
053fd49d PB |
731 | /* |
732 | * Special case for TM. We send a WILL, but | |
733 | * pretend we sent a WONT. | |
734 | */ | |
735 | send_will(option, 0); | |
736 | mywants[option] = OPT_NO; | |
737 | myopts[option] = OPT_NO; | |
738 | return; | |
739 | ||
ea139302 PB |
740 | case TELOPT_LINEMODE: |
741 | case TELOPT_TTYPE: | |
742 | case TELOPT_NAWS: | |
743 | case TELOPT_TSPEED: | |
744 | case TELOPT_LFLOW: | |
745 | default: | |
746 | break; | |
747 | } | |
053fd49d | 748 | if (changeok) { |
ea139302 | 749 | mywants[option] = OPT_YES; |
053fd49d PB |
750 | send_will(option, 0); |
751 | } else { | |
752 | will_wont_resp[option]++; | |
753 | send_wont(option, 0); | |
ea139302 | 754 | } |
ea139302 | 755 | } |
053fd49d | 756 | myopts[option] = OPT_YES; |
ea139302 PB |
757 | |
758 | } /* end of dooption */ | |
759 | ||
053fd49d PB |
760 | send_wont(option, init) |
761 | int option, init; | |
ea139302 | 762 | { |
053fd49d PB |
763 | if (init) { |
764 | if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) || | |
765 | mywants[option] == OPT_NO) | |
766 | return; | |
767 | mywants[option] = OPT_NO; | |
768 | will_wont_resp[option]++; | |
769 | } | |
770 | (void) sprintf(nfrontp, wont, option); | |
771 | nfrontp += sizeof (wont) - 2; | |
772 | } | |
ea139302 | 773 | |
053fd49d PB |
774 | dontoption(option) |
775 | int option; | |
776 | { | |
ea139302 PB |
777 | /* |
778 | * Process client input. | |
779 | */ | |
053fd49d PB |
780 | if (will_wont_resp[option]) { |
781 | will_wont_resp[option]--; | |
782 | if (will_wont_resp[option] && myopts[option] == OPT_NO) | |
783 | will_wont_resp[option]--; | |
784 | } | |
785 | if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) { | |
ea139302 PB |
786 | switch (option) { |
787 | case TELOPT_BINARY: | |
788 | init_termbuf(); | |
789 | tty_binaryout(0); | |
790 | set_termbuf(); | |
791 | break; | |
792 | ||
053fd49d | 793 | case TELOPT_ECHO: /* we should stop echoing */ |
ea139302 | 794 | #ifdef LINEMODE |
053fd49d | 795 | if (lmodetype == NO_LINEMODE) { |
ea139302 PB |
796 | #endif |
797 | init_termbuf(); | |
798 | tty_setecho(0); | |
799 | set_termbuf(); | |
800 | #ifdef LINEMODE | |
801 | } | |
802 | #endif | |
803 | break; | |
804 | ||
805 | case TELOPT_SGA: | |
806 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
807 | /* | |
053fd49d PB |
808 | * If kludge linemode is in use, then we |
809 | * must process an incoming do SGA for | |
810 | * linemode purposes. | |
ea139302 PB |
811 | */ |
812 | if (lmodetype == KLUDGE_LINEMODE) { | |
813 | /* | |
053fd49d PB |
814 | * The client is asking us to turn |
815 | * linemode on. | |
ea139302 PB |
816 | */ |
817 | clientstat(TELOPT_LINEMODE, WILL, 0); | |
818 | /* | |
053fd49d PB |
819 | * If we did not turn line mode on, |
820 | * then what do we say? Will SGA? | |
821 | * This violates design of telnet. | |
822 | * Gross. Very Gross. | |
ea139302 PB |
823 | */ |
824 | } | |
825 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
826 | ||
827 | default: | |
828 | break; | |
829 | } | |
ea139302 | 830 | |
053fd49d PB |
831 | mywants[option] = OPT_NO; |
832 | send_wont(option, 0); | |
ea139302 | 833 | } |
053fd49d | 834 | myopts[option] = OPT_NO; |
ea139302 PB |
835 | |
836 | } /* end of dontoption */ | |
837 | ||
838 | /* | |
839 | * suboption() | |
840 | * | |
841 | * Look at the sub-option buffer, and try to be helpful to the other | |
842 | * side. | |
843 | * | |
844 | * Currently we recognize: | |
845 | * | |
846 | * Terminal type is | |
847 | * Linemode | |
848 | * Window size | |
849 | * Terminal speed | |
850 | */ | |
851 | suboption() | |
852 | { | |
853 | register int subchar; | |
854 | ||
855 | subchar = SB_GET(); | |
856 | switch (subchar) { | |
857 | case TELOPT_TSPEED: { | |
858 | register int xspeed, rspeed; | |
859 | ||
860 | if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ | |
861 | break; | |
862 | ||
863 | settimer(tspeedsubopt); | |
864 | ||
865 | if (SB_EOF() || SB_GET() != TELQUAL_IS) | |
866 | return; | |
867 | ||
868 | xspeed = atoi(subpointer); | |
869 | ||
870 | while (SB_GET() != ',' && !SB_EOF()); | |
871 | if (SB_EOF()) | |
872 | return; | |
873 | ||
874 | rspeed = atoi(subpointer); | |
875 | clientstat(TELOPT_TSPEED, xspeed, rspeed); | |
876 | ||
877 | break; | |
878 | ||
879 | } /* end of case TELOPT_TSPEED */ | |
880 | ||
881 | case TELOPT_TTYPE: { /* Yaaaay! */ | |
882 | static char terminalname[5+41] = "TERM="; | |
883 | ||
ca8b2d5c | 884 | if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */ |
ea139302 PB |
885 | break; |
886 | settimer(ttypesubopt); | |
887 | ||
888 | if (SB_GET() != TELQUAL_IS) { | |
889 | return; /* ??? XXX but, this is the most robust */ | |
890 | } | |
891 | ||
892 | terminaltype = terminalname+sizeof("TERM=")-1; | |
893 | ||
894 | while ((terminaltype < (terminalname + sizeof terminalname-1)) && | |
895 | !SB_EOF()) { | |
896 | register int c; | |
897 | ||
898 | c = SB_GET(); | |
899 | if (isupper(c)) { | |
900 | c = tolower(c); | |
901 | } | |
902 | *terminaltype++ = c; /* accumulate name */ | |
903 | } | |
904 | *terminaltype = 0; | |
905 | terminaltype = terminalname; | |
906 | break; | |
907 | } /* end of case TELOPT_TTYPE */ | |
908 | ||
909 | case TELOPT_NAWS: { | |
910 | register int xwinsize, ywinsize; | |
911 | ||
912 | if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ | |
913 | break; | |
914 | ||
915 | if (SB_EOF()) | |
916 | return; | |
917 | xwinsize = SB_GET() << 8; | |
918 | if (SB_EOF()) | |
919 | return; | |
920 | xwinsize |= SB_GET(); | |
921 | if (SB_EOF()) | |
922 | return; | |
923 | ywinsize = SB_GET() << 8; | |
924 | if (SB_EOF()) | |
925 | return; | |
926 | ywinsize |= SB_GET(); | |
927 | clientstat(TELOPT_NAWS, xwinsize, ywinsize); | |
928 | ||
929 | break; | |
930 | ||
931 | } /* end of case TELOPT_NAWS */ | |
932 | ||
933 | #ifdef LINEMODE | |
934 | case TELOPT_LINEMODE: { | |
935 | register int request; | |
936 | ||
937 | if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ | |
938 | break; | |
939 | /* | |
940 | * Process linemode suboptions. | |
941 | */ | |
942 | if (SB_EOF()) break; /* garbage was sent */ | |
943 | request = SB_GET(); /* get will/wont */ | |
944 | if (SB_EOF()) break; /* another garbage check */ | |
945 | ||
946 | if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ | |
947 | /* | |
948 | * Process suboption buffer of slc's | |
949 | */ | |
950 | start_slc(1); | |
951 | do_opt_slc(subpointer, subend - subpointer); | |
952 | end_slc(0); | |
953 | ||
954 | } else if (request == LM_MODE) { | |
955 | useeditmode = SB_GET(); /* get mode flag */ | |
956 | clientstat(LM_MODE, 0, 0); | |
957 | } | |
958 | ||
959 | switch (SB_GET()) { /* what suboption? */ | |
960 | case LM_FORWARDMASK: | |
961 | /* | |
962 | * According to spec, only server can send request for | |
963 | * forwardmask, and client can only return a positive response. | |
964 | * So don't worry about it. | |
965 | */ | |
966 | ||
967 | default: | |
968 | break; | |
969 | } | |
970 | ||
971 | } /* end of case TELOPT_LINEMODE */ | |
972 | #endif | |
973 | case TELOPT_STATUS: { | |
974 | int mode; | |
975 | ||
976 | mode = SB_GET(); | |
977 | switch (mode) { | |
978 | case TELQUAL_SEND: | |
979 | if (myopts[TELOPT_STATUS] == OPT_YES) | |
980 | send_status(); | |
981 | break; | |
982 | ||
983 | case TELQUAL_IS: | |
984 | break; | |
985 | ||
986 | default: | |
987 | break; | |
988 | } | |
989 | } | |
990 | ||
991 | default: | |
992 | break; | |
993 | } /* end of switch */ | |
994 | ||
995 | } /* end of suboption */ | |
996 | ||
997 | #define ADD(c) *ncp++ = c; | |
998 | #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } | |
999 | send_status() | |
1000 | { | |
1001 | char statusbuf[256]; | |
1002 | register char *ncp; | |
1003 | register int i; | |
1004 | ||
1005 | ncp = statusbuf; | |
1006 | ||
1007 | netflush(); /* get rid of anything waiting to go out */ | |
1008 | ||
1009 | ADD(IAC); | |
1010 | ADD(SB); | |
1011 | ADD(TELOPT_STATUS); | |
1012 | ADD(TELQUAL_IS); | |
1013 | ||
1014 | for (i = 0; i < NTELOPTS; i++) { | |
1015 | if (myopts[i] == OPT_YES) { | |
1016 | ADD(WILL); | |
1017 | ADD_DATA(i); | |
1018 | if (i == IAC) | |
1019 | ADD(IAC); | |
1020 | } | |
1021 | if (hisopts[i] == OPT_YES) { | |
1022 | ADD(DO); | |
1023 | ADD_DATA(i); | |
1024 | if (i == IAC) | |
1025 | ADD(IAC); | |
1026 | } | |
1027 | } | |
1028 | ||
1029 | #ifdef LINEMODE | |
1030 | if (hisopts[TELOPT_LINEMODE] == OPT_YES) { | |
1031 | char *cp, *cpe; | |
1032 | int len; | |
1033 | ||
1034 | ADD(SB); | |
1035 | ADD(TELOPT_LINEMODE); | |
1036 | ADD(LM_MODE); | |
1037 | ADD_DATA(editmode); | |
1038 | if (editmode == IAC) | |
1039 | ADD(IAC); | |
1040 | ADD(SE); | |
1041 | ||
1042 | ADD(SB); | |
1043 | ADD(TELOPT_LINEMODE); | |
1044 | ADD(LM_SLC); | |
1045 | start_slc(0); | |
1046 | send_slc(); | |
1047 | len = end_slc(&cp); | |
1048 | for (cpe = cp + len; cp < cpe; cp++) | |
1049 | ADD_DATA(*cp); | |
1050 | ADD(SE); | |
1051 | } | |
1052 | #endif /* LINEMODE */ | |
1053 | ||
1054 | ADD(IAC); | |
1055 | ADD(SE); | |
1056 | ||
1057 | writenet(statusbuf, ncp - statusbuf); | |
1058 | netflush(); /* Send it on its way */ | |
1059 | } |