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