Commit | Line | Data |
---|---|---|
897ce52e KB |
1 | /* |
2 | * Copyright (c) 1988 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 this notice is preserved and that due credit is given | |
7 | * to the University of California at Berkeley. The name of the University | |
8 | * may not be used to endorse or promote products derived from this | |
9 | * software without specific prior written permission. This software | |
10 | * is provided ``as is'' without express or implied warranty. | |
11 | */ | |
22e155fc | 12 | |
f18adf63 | 13 | #ifndef lint |
80a47e22 | 14 | static char sccsid[] = "@(#)telnet.c 5.32 (Berkeley) %G%"; |
897ce52e | 15 | #endif /* not lint */ |
f18adf63 | 16 | |
de3b21e8 | 17 | #include <sys/types.h> |
de3b21e8 | 18 | |
890e6cf8 | 19 | #if defined(unix) |
2e48960f | 20 | #include <signal.h> |
890e6cf8 GM |
21 | /* By the way, we need to include curses.h before telnet.h since, |
22 | * among other things, telnet.h #defines 'DO', which is a variable | |
23 | * declared in curses.h. | |
24 | */ | |
25 | #include <curses.h> | |
26 | #endif /* defined(unix) */ | |
27 | ||
9c1dab9e | 28 | #include <arpa/telnet.h> |
890e6cf8 | 29 | |
890e6cf8 | 30 | #if defined(unix) |
f2bf20fe | 31 | #include <strings.h> |
890e6cf8 GM |
32 | #else /* defined(unix) */ |
33 | #include <string.h> | |
34 | #endif /* defined(unix) */ | |
de3b21e8 | 35 | |
115a5494 GM |
36 | #include "ring.h" |
37 | ||
890e6cf8 GM |
38 | #include "defines.h" |
39 | #include "externs.h" | |
40 | #include "types.h" | |
41 | #include "general.h" | |
040b6435 | 42 | |
040b6435 | 43 | \f |
792ffc71 | 44 | #define strip(x) ((x)&0x7f) |
890e6cf8 | 45 | |
a19db822 | 46 | |
890e6cf8 GM |
47 | static char subbuffer[SUBBUFSIZE], |
48 | *subpointer, *subend; /* buffer for sub-options */ | |
0ea293c6 GM |
49 | #define SB_CLEAR() subpointer = subbuffer; |
50 | #define SB_TERM() subend = subpointer; | |
51 | #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ | |
52 | *subpointer++ = (c); \ | |
53 | } | |
54 | ||
a19db822 BJ |
55 | char hisopts[256]; |
56 | char myopts[256]; | |
57 | ||
58 | char doopt[] = { IAC, DO, '%', 'c', 0 }; | |
59 | char dont[] = { IAC, DONT, '%', 'c', 0 }; | |
60 | char will[] = { IAC, WILL, '%', 'c', 0 }; | |
61 | char wont[] = { IAC, WONT, '%', 'c', 0 }; | |
62 | ||
890e6cf8 GM |
63 | int |
64 | connected, | |
890e6cf8 GM |
65 | showoptions, |
66 | In3270, /* Are we in 3270 mode? */ | |
67 | ISend, /* trying to send network data in */ | |
68 | debug = 0, | |
69 | crmod, | |
70 | netdata, /* Print out network data flow */ | |
71 | crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ | |
80a47e22 | 72 | #if defined(TN3270) |
890e6cf8 GM |
73 | noasynch = 0, /* User specified "-noasynch" on command line */ |
74 | askedSGA = 0, /* We have talked about suppress go ahead */ | |
80a47e22 | 75 | #endif /* defined(TN3270) */ |
448f9c06 | 76 | telnetport, |
b307f09e GM |
77 | SYNCHing, /* we are in TELNET SYNCH mode */ |
78 | flushout, /* flush output */ | |
79 | autoflush = 0, /* flush output when interrupting? */ | |
80 | autosynch, /* send interrupt characters with SYNCH? */ | |
81 | localchars, /* we recognize interrupt/quit */ | |
82 | donelclchars, /* the user has set "localchars" */ | |
83 | donebinarytoggle, /* the user has put us in binary */ | |
84 | dontlecho, /* do we suppress local echoing right now? */ | |
85 | globalmode; | |
890e6cf8 GM |
86 | |
87 | #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ | |
88 | ||
89 | char | |
90 | *prompt = 0, | |
91 | escape, | |
92 | echoc; | |
93 | ||
890e6cf8 GM |
94 | /* |
95 | * Telnet receiver states for fsm | |
96 | */ | |
97 | #define TS_DATA 0 | |
98 | #define TS_IAC 1 | |
99 | #define TS_WILL 2 | |
100 | #define TS_WONT 3 | |
101 | #define TS_DO 4 | |
102 | #define TS_DONT 5 | |
103 | #define TS_CR 6 | |
104 | #define TS_SB 7 /* sub-option collection */ | |
105 | #define TS_SE 8 /* looking for sub-option end */ | |
a19db822 | 106 | |
890e6cf8 | 107 | static int telrcv_state; |
a19db822 | 108 | |
890e6cf8 GM |
109 | jmp_buf toplevel = { 0 }; |
110 | jmp_buf peerdied; | |
40e8c2a3 | 111 | |
890e6cf8 | 112 | int flushline; |
40e8c2a3 GM |
113 | |
114 | /* | |
115 | * The following are some clocks used to decide how to interpret | |
040b6435 | 116 | * the relationship between various variables. |
40e8c2a3 GM |
117 | */ |
118 | ||
890e6cf8 GM |
119 | Clocks clocks; |
120 | \f | |
121 | Modelist modelist[] = { | |
122 | { "telnet command mode", COMMAND_LINE }, | |
123 | { "character-at-a-time mode", 0 }, | |
124 | { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, | |
125 | { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, | |
126 | { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, | |
127 | { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, | |
128 | { "3270 mode", 0 }, | |
129 | }; | |
40e8c2a3 | 130 | |
40e8c2a3 GM |
131 | \f |
132 | /* | |
890e6cf8 | 133 | * Initialize telnet environment. |
40e8c2a3 | 134 | */ |
3e16c811 | 135 | |
890e6cf8 GM |
136 | init_telnet() |
137 | { | |
890e6cf8 GM |
138 | SB_CLEAR(); |
139 | ClearArray(hisopts); | |
140 | ClearArray(myopts); | |
f2bf20fe | 141 | |
f8362f44 | 142 | connected = In3270 = ISend = donebinarytoggle = 0; |
f2bf20fe | 143 | |
890e6cf8 GM |
144 | #if defined(unix) && defined(TN3270) |
145 | HaveInput = 0; | |
146 | #endif /* defined(unix) && defined(TN3270) */ | |
a19db822 | 147 | |
890e6cf8 GM |
148 | SYNCHing = 0; |
149 | ||
890e6cf8 GM |
150 | /* Don't change NetTrace */ |
151 | ||
152 | escape = CONTROL(']'); | |
153 | echoc = CONTROL('E'); | |
154 | ||
155 | flushline = 1; | |
156 | telrcv_state = TS_DATA; | |
a19db822 | 157 | } |
f8362f44 GM |
158 | \f |
159 | ||
160 | #include <varargs.h> | |
161 | ||
80a47e22 | 162 | /*VARARGS*/ |
f8362f44 GM |
163 | static void |
164 | printring(va_alist) | |
165 | va_dcl | |
166 | { | |
167 | va_list ap; | |
168 | char buffer[100]; /* where things go */ | |
169 | char *ptr; | |
170 | char *format; | |
171 | char *string; | |
172 | Ring *ring; | |
173 | int i; | |
174 | ||
175 | va_start(ap); | |
176 | ||
177 | ring = va_arg(ap, Ring *); | |
178 | format = va_arg(ap, char *); | |
179 | ptr = buffer; | |
180 | ||
181 | while ((i = *format++) != 0) { | |
182 | if (i == '%') { | |
183 | i = *format++; | |
184 | switch (i) { | |
185 | case 'c': | |
186 | *ptr++ = va_arg(ap, int); | |
187 | break; | |
188 | case 's': | |
189 | string = va_arg(ap, char *); | |
190 | ring_supply_data(ring, buffer, ptr-buffer); | |
191 | ring_supply_data(ring, string, strlen(string)); | |
192 | ptr = buffer; | |
193 | break; | |
194 | case 0: | |
195 | ExitString("printring: trailing %%.\n", 1); | |
196 | /*NOTREACHED*/ | |
197 | default: | |
198 | ExitString("printring: unknown format character.\n", 1); | |
199 | /*NOTREACHED*/ | |
200 | } | |
201 | } else { | |
202 | *ptr++ = i; | |
203 | } | |
204 | } | |
205 | ring_supply_data(ring, buffer, ptr-buffer); | |
206 | } | |
a19db822 | 207 | |
40e8c2a3 | 208 | |
890e6cf8 GM |
209 | void |
210 | willoption(option, reply) | |
211 | int option, reply; | |
40e8c2a3 | 212 | { |
890e6cf8 GM |
213 | char *fmt; |
214 | ||
215 | switch (option) { | |
216 | ||
217 | case TELOPT_ECHO: | |
218 | # if defined(TN3270) | |
219 | /* | |
220 | * The following is a pain in the rear-end. | |
221 | * Various IBM servers (some versions of Wiscnet, | |
222 | * possibly Fibronics/Spartacus, and who knows who | |
223 | * else) will NOT allow us to send "DO SGA" too early | |
224 | * in the setup proceedings. On the other hand, | |
225 | * 4.2 servers (telnetd) won't set SGA correctly. | |
226 | * So, we are stuck. Empirically (but, based on | |
227 | * a VERY small sample), the IBM servers don't send | |
228 | * out anything about ECHO, so we postpone our sending | |
229 | * "DO SGA" until we see "WILL ECHO" (which 4.2 servers | |
230 | * DO send). | |
231 | */ | |
232 | { | |
233 | if (askedSGA == 0) { | |
234 | askedSGA = 1; | |
235 | if (!hisopts[TELOPT_SGA]) { | |
236 | willoption(TELOPT_SGA, 0); | |
237 | } | |
40e8c2a3 | 238 | } |
890e6cf8 GM |
239 | } |
240 | /* Fall through */ | |
241 | case TELOPT_EOR: | |
242 | case TELOPT_BINARY: | |
243 | #endif /* defined(TN3270) */ | |
244 | case TELOPT_SGA: | |
245 | settimer(modenegotiated); | |
246 | hisopts[option] = 1; | |
247 | fmt = doopt; | |
248 | setconnmode(); /* possibly set new tty mode */ | |
40e8c2a3 | 249 | break; |
890e6cf8 GM |
250 | |
251 | case TELOPT_TM: | |
252 | return; /* Never reply to TM will's/wont's */ | |
253 | ||
40e8c2a3 | 254 | default: |
890e6cf8 | 255 | fmt = dont; |
40e8c2a3 GM |
256 | break; |
257 | } | |
f8362f44 | 258 | printring(&netoring, fmt, option); |
890e6cf8 GM |
259 | if (reply) |
260 | printoption(">SENT", fmt, option, reply); | |
261 | else | |
262 | printoption("<SENT", fmt, option, reply); | |
40e8c2a3 | 263 | } |
f2bf20fe | 264 | |
890e6cf8 GM |
265 | void |
266 | wontoption(option, reply) | |
267 | int option, reply; | |
f2bf20fe | 268 | { |
890e6cf8 | 269 | char *fmt; |
0ea293c6 | 270 | |
890e6cf8 | 271 | switch (option) { |
0ea293c6 | 272 | |
890e6cf8 GM |
273 | case TELOPT_ECHO: |
274 | case TELOPT_SGA: | |
275 | settimer(modenegotiated); | |
276 | hisopts[option] = 0; | |
277 | fmt = dont; | |
278 | setconnmode(); /* Set new tty mode */ | |
279 | break; | |
0ea293c6 | 280 | |
890e6cf8 GM |
281 | case TELOPT_TM: |
282 | return; /* Never reply to TM will's/wont's */ | |
0ea293c6 | 283 | |
890e6cf8 GM |
284 | default: |
285 | fmt = dont; | |
0ea293c6 | 286 | } |
f8362f44 | 287 | printring(&netoring, fmt, option); |
890e6cf8 GM |
288 | if (reply) |
289 | printoption(">SENT", fmt, option, reply); | |
290 | else | |
291 | printoption("<SENT", fmt, option, reply); | |
0ea293c6 | 292 | } |
f2bf20fe | 293 | |
890e6cf8 GM |
294 | static void |
295 | dooption(option) | |
296 | int option; | |
f2bf20fe | 297 | { |
890e6cf8 | 298 | char *fmt; |
f2bf20fe | 299 | |
890e6cf8 | 300 | switch (option) { |
f2bf20fe | 301 | |
890e6cf8 GM |
302 | case TELOPT_TM: |
303 | fmt = will; | |
304 | break; | |
f2bf20fe | 305 | |
890e6cf8 GM |
306 | # if defined(TN3270) |
307 | case TELOPT_EOR: | |
308 | case TELOPT_BINARY: | |
309 | # endif /* defined(TN3270) */ | |
310 | case TELOPT_TTYPE: /* terminal type option */ | |
311 | case TELOPT_SGA: /* no big deal */ | |
312 | fmt = will; | |
313 | myopts[option] = 1; | |
314 | break; | |
f2bf20fe | 315 | |
890e6cf8 GM |
316 | case TELOPT_ECHO: /* We're never going to echo... */ |
317 | default: | |
318 | fmt = wont; | |
319 | break; | |
f2bf20fe | 320 | } |
f8362f44 | 321 | printring(&netoring, fmt, option); |
890e6cf8 | 322 | printoption(">SENT", fmt, option, 0); |
f2bf20fe | 323 | } |
3087f2df | 324 | |
3087f2df | 325 | /* |
890e6cf8 | 326 | * suboption() |
3087f2df | 327 | * |
890e6cf8 GM |
328 | * Look at the sub-option buffer, and try to be helpful to the other |
329 | * side. | |
3087f2df | 330 | * |
890e6cf8 | 331 | * Currently we recognize: |
3087f2df | 332 | * |
890e6cf8 | 333 | * Terminal type, send request. |
3087f2df GM |
334 | */ |
335 | ||
890e6cf8 GM |
336 | static void |
337 | suboption() | |
3087f2df | 338 | { |
890e6cf8 GM |
339 | printsub("<", subbuffer, subend-subbuffer+1); |
340 | switch (subbuffer[0]&0xff) { | |
341 | case TELOPT_TTYPE: | |
342 | if ((subbuffer[1]&0xff) != TELQUAL_SEND) { | |
343 | ; | |
3087f2df | 344 | } else { |
890e6cf8 GM |
345 | char *name; |
346 | char namebuf[41]; | |
347 | extern char *getenv(); | |
348 | int len; | |
f2bf20fe | 349 | |
890e6cf8 | 350 | #if defined(TN3270) |
b307f09e | 351 | if (tn3270_ttype()) { |
890e6cf8 GM |
352 | return; |
353 | } | |
354 | #endif /* defined(TN3270) */ | |
890e6cf8 GM |
355 | name = getenv("TERM"); |
356 | if ((name == 0) || ((len = strlen(name)) > 40)) { | |
357 | name = "UNKNOWN"; | |
a044ddbc | 358 | len = strlen(name); |
890e6cf8 GM |
359 | } |
360 | if ((len + 4+2) < NETROOM()) { | |
361 | strcpy(namebuf, name); | |
362 | upcase(namebuf); | |
f8362f44 | 363 | printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, |
890e6cf8 | 364 | TELQUAL_IS, namebuf, IAC, SE); |
115a5494 GM |
365 | /* XXX */ |
366 | /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ | |
890e6cf8 | 367 | } else { |
115a5494 | 368 | ExitString("No room in buffer for terminal type.\n", |
890e6cf8 GM |
369 | 1); |
370 | /*NOTREACHED*/ | |
371 | } | |
f2bf20fe | 372 | } |
40e8c2a3 | 373 | |
890e6cf8 GM |
374 | default: |
375 | break; | |
40e8c2a3 | 376 | } |
40e8c2a3 | 377 | } |
40e8c2a3 | 378 | \f |
f2bf20fe | 379 | |
2e48960f | 380 | int |
890e6cf8 | 381 | telrcv() |
f2bf20fe | 382 | { |
890e6cf8 | 383 | register int c; |
d5c9ed55 GM |
384 | register int scc; |
385 | register char *sbp; | |
386 | int count; | |
387 | int returnValue = 0; | |
388 | ||
389 | scc = 0; | |
390 | count = 0; | |
391 | while (TTYROOM() > 2) { | |
392 | if (scc == 0) { | |
393 | if (count) { | |
8b6750f5 | 394 | ring_consumed(&netiring, count); |
d5c9ed55 GM |
395 | returnValue = 1; |
396 | count = 0; | |
397 | } | |
8b6750f5 GM |
398 | sbp = netiring.consume; |
399 | scc = ring_full_consecutive(&netiring); | |
d5c9ed55 GM |
400 | if (scc == 0) { |
401 | /* No more data coming in */ | |
402 | break; | |
403 | } | |
404 | } | |
405 | ||
406 | c = *sbp++ & 0xff, scc--; count++; | |
890e6cf8 | 407 | |
890e6cf8 GM |
408 | switch (telrcv_state) { |
409 | ||
410 | case TS_CR: | |
411 | telrcv_state = TS_DATA; | |
412 | if (c == '\0') { | |
413 | break; /* Ignore \0 after CR */ | |
414 | } else if (c == '\n') { | |
807a3a7d | 415 | if ((!hisopts[TELOPT_ECHO]) && !crmod) { |
890e6cf8 | 416 | TTYADD(c); |
40e8c2a3 | 417 | } |
3e16c811 | 418 | break; |
0ea293c6 | 419 | } |
890e6cf8 GM |
420 | /* Else, fall through */ |
421 | ||
422 | case TS_DATA: | |
423 | if (c == IAC) { | |
424 | telrcv_state = TS_IAC; | |
2e48960f | 425 | break; |
0ea293c6 | 426 | } |
890e6cf8 GM |
427 | # if defined(TN3270) |
428 | if (In3270) { | |
429 | *Ifrontp++ = c; | |
d5c9ed55 GM |
430 | while (scc > 0) { |
431 | c = *sbp++ & 0377, scc--; count++; | |
890e6cf8 GM |
432 | if (c == IAC) { |
433 | telrcv_state = TS_IAC; | |
eb1b2833 | 434 | break; |
890e6cf8 GM |
435 | } |
436 | *Ifrontp++ = c; | |
40e8c2a3 | 437 | } |
890e6cf8 GM |
438 | } else |
439 | # endif /* defined(TN3270) */ | |
440 | /* | |
441 | * The 'crmod' hack (see following) is needed | |
442 | * since we can't * set CRMOD on output only. | |
443 | * Machines like MULTICS like to send \r without | |
444 | * \n; since we must turn off CRMOD to get proper | |
445 | * input, the mapping is done here (sigh). | |
446 | */ | |
447 | if ((c == '\r') && !hisopts[TELOPT_BINARY]) { | |
448 | if (scc > 0) { | |
449 | c = *sbp&0xff; | |
450 | if (c == 0) { | |
d5c9ed55 | 451 | sbp++, scc--; count++; |
890e6cf8 GM |
452 | /* a "true" CR */ |
453 | TTYADD('\r'); | |
454 | } else if (!hisopts[TELOPT_ECHO] && | |
455 | (c == '\n')) { | |
d5c9ed55 | 456 | sbp++, scc--; count++; |
890e6cf8 | 457 | TTYADD('\n'); |
040b6435 | 458 | } else { |
890e6cf8 GM |
459 | TTYADD('\r'); |
460 | if (crmod) { | |
461 | TTYADD('\n'); | |
462 | } | |
040b6435 | 463 | } |
890e6cf8 GM |
464 | } else { |
465 | telrcv_state = TS_CR; | |
466 | TTYADD('\r'); | |
467 | if (crmod) { | |
468 | TTYADD('\n'); | |
792ffc71 | 469 | } |
a19db822 | 470 | } |
890e6cf8 GM |
471 | } else { |
472 | TTYADD(c); | |
473 | } | |
474 | continue; | |
475 | ||
476 | case TS_IAC: | |
477 | switch (c) { | |
478 | ||
479 | case WILL: | |
480 | telrcv_state = TS_WILL; | |
481 | continue; | |
482 | ||
483 | case WONT: | |
484 | telrcv_state = TS_WONT; | |
485 | continue; | |
486 | ||
487 | case DO: | |
488 | telrcv_state = TS_DO; | |
489 | continue; | |
490 | ||
491 | case DONT: | |
492 | telrcv_state = TS_DONT; | |
493 | continue; | |
494 | ||
495 | case DM: | |
496 | /* | |
497 | * We may have missed an urgent notification, | |
498 | * so make sure we flush whatever is in the | |
499 | * buffer currently. | |
500 | */ | |
501 | SYNCHing = 1; | |
502 | ttyflush(1); | |
f8362f44 | 503 | SYNCHing = stilloob(); |
890e6cf8 | 504 | settimer(gotDM); |
a19db822 BJ |
505 | break; |
506 | ||
890e6cf8 GM |
507 | case NOP: |
508 | case GA: | |
a19db822 | 509 | break; |
3087f2df | 510 | |
890e6cf8 GM |
511 | case SB: |
512 | SB_CLEAR(); | |
513 | telrcv_state = TS_SB; | |
514 | continue; | |
515 | ||
516 | # if defined(TN3270) | |
517 | case EOR: | |
518 | if (In3270) { | |
519 | Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); | |
520 | if (Ibackp == Ifrontp) { | |
521 | Ibackp = Ifrontp = Ibuf; | |
522 | ISend = 0; /* should have been! */ | |
523 | } else { | |
524 | ISend = 1; | |
3e16c811 GM |
525 | } |
526 | } | |
527 | break; | |
890e6cf8 GM |
528 | # endif /* defined(TN3270) */ |
529 | ||
530 | case IAC: | |
531 | # if !defined(TN3270) | |
532 | TTYADD(IAC); | |
533 | # else /* !defined(TN3270) */ | |
534 | if (In3270) { | |
535 | *Ifrontp++ = IAC; | |
536 | } else { | |
537 | TTYADD(IAC); | |
538 | } | |
539 | # endif /* !defined(TN3270) */ | |
3e16c811 | 540 | break; |
890e6cf8 | 541 | |
3e16c811 | 542 | default: |
3e16c811 GM |
543 | break; |
544 | } | |
890e6cf8 GM |
545 | telrcv_state = TS_DATA; |
546 | continue; | |
547 | ||
548 | case TS_WILL: | |
549 | printoption(">RCVD", will, c, !hisopts[c]); | |
550 | if (c == TELOPT_TM) { | |
551 | if (flushout) { | |
552 | flushout = 0; | |
553 | } | |
554 | } else if (!hisopts[c]) { | |
555 | willoption(c, 1); | |
f2bf20fe | 556 | } |
890e6cf8 GM |
557 | SetIn3270(); |
558 | telrcv_state = TS_DATA; | |
559 | continue; | |
560 | ||
561 | case TS_WONT: | |
562 | printoption(">RCVD", wont, c, hisopts[c]); | |
563 | if (c == TELOPT_TM) { | |
564 | if (flushout) { | |
565 | flushout = 0; | |
566 | } | |
567 | } else if (hisopts[c]) { | |
568 | wontoption(c, 1); | |
f2bf20fe | 569 | } |
890e6cf8 GM |
570 | SetIn3270(); |
571 | telrcv_state = TS_DATA; | |
572 | continue; | |
573 | ||
574 | case TS_DO: | |
575 | printoption(">RCVD", doopt, c, !myopts[c]); | |
576 | if (!myopts[c]) | |
577 | dooption(c); | |
578 | SetIn3270(); | |
579 | telrcv_state = TS_DATA; | |
580 | continue; | |
581 | ||
582 | case TS_DONT: | |
583 | printoption(">RCVD", dont, c, myopts[c]); | |
584 | if (myopts[c]) { | |
585 | myopts[c] = 0; | |
f8362f44 | 586 | printring(&netoring, wont, c); |
890e6cf8 GM |
587 | flushline = 1; |
588 | setconnmode(); /* set new tty mode (maybe) */ | |
589 | printoption(">SENT", wont, c, 0); | |
590 | } | |
591 | SetIn3270(); | |
592 | telrcv_state = TS_DATA; | |
593 | continue; | |
40e8c2a3 | 594 | |
890e6cf8 GM |
595 | case TS_SB: |
596 | if (c == IAC) { | |
597 | telrcv_state = TS_SE; | |
598 | } else { | |
599 | SB_ACCUM(c); | |
3087f2df | 600 | } |
890e6cf8 | 601 | continue; |
40e8c2a3 | 602 | |
890e6cf8 GM |
603 | case TS_SE: |
604 | if (c != SE) { | |
605 | if (c != IAC) { | |
606 | SB_ACCUM(IAC); | |
607 | } | |
608 | SB_ACCUM(c); | |
609 | telrcv_state = TS_SB; | |
610 | } else { | |
611 | SB_TERM(); | |
612 | suboption(); /* handle sub-option */ | |
613 | SetIn3270(); | |
614 | telrcv_state = TS_DATA; | |
615 | } | |
40e8c2a3 | 616 | } |
40e8c2a3 | 617 | } |
ad1c581e GM |
618 | if (count) |
619 | ring_consumed(&netiring, count); | |
d5c9ed55 GM |
620 | return returnValue||count; |
621 | } | |
622 | ||
623 | static int | |
f8362f44 | 624 | telsnd() |
d5c9ed55 GM |
625 | { |
626 | int tcc; | |
627 | int count; | |
628 | int returnValue = 0; | |
629 | char *tbp; | |
630 | ||
631 | tcc = 0; | |
632 | count = 0; | |
633 | while (NETROOM() > 2) { | |
634 | register int sc; | |
635 | register int c; | |
636 | ||
637 | if (tcc == 0) { | |
638 | if (count) { | |
8b6750f5 | 639 | ring_consumed(&ttyiring, count); |
d5c9ed55 GM |
640 | returnValue = 1; |
641 | count = 0; | |
642 | } | |
8b6750f5 GM |
643 | tbp = ttyiring.consume; |
644 | tcc = ring_full_consecutive(&ttyiring); | |
d5c9ed55 GM |
645 | if (tcc == 0) { |
646 | break; | |
647 | } | |
648 | } | |
649 | c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; | |
650 | if (sc == escape) { | |
651 | command(0); | |
652 | tcc = 0; | |
653 | flushline = 1; | |
654 | break; | |
655 | } else if (MODE_LINE(globalmode) && (sc == echoc)) { | |
656 | if (tcc > 0 && strip(*tbp) == echoc) { | |
657 | tcc--; tbp++; count++; | |
658 | } else { | |
659 | dontlecho = !dontlecho; | |
660 | settimer(echotoggle); | |
661 | setconnmode(); | |
662 | flushline = 1; | |
663 | break; | |
664 | } | |
665 | } | |
666 | if (localchars) { | |
667 | if (TerminalSpecialChars(sc) == 0) { | |
668 | break; | |
669 | } | |
670 | } | |
671 | if (!myopts[TELOPT_BINARY]) { | |
672 | switch (c) { | |
673 | case '\n': | |
674 | /* | |
675 | * If we are in CRMOD mode (\r ==> \n) | |
676 | * on our local machine, then probably | |
677 | * a newline (unix) is CRLF (TELNET). | |
678 | */ | |
679 | if (MODE_LOCAL_CHARS(globalmode)) { | |
680 | NETADD('\r'); | |
681 | } | |
682 | NETADD('\n'); | |
683 | flushline = 1; | |
684 | break; | |
685 | case '\r': | |
686 | if (!crlf) { | |
687 | NET2ADD('\r', '\0'); | |
688 | } else { | |
689 | NET2ADD('\r', '\n'); | |
690 | } | |
691 | flushline = 1; | |
692 | break; | |
693 | case IAC: | |
694 | NET2ADD(IAC, IAC); | |
695 | break; | |
696 | default: | |
697 | NETADD(c); | |
698 | break; | |
699 | } | |
700 | } else if (c == IAC) { | |
701 | NET2ADD(IAC, IAC); | |
702 | } else { | |
703 | NETADD(c); | |
704 | } | |
705 | } | |
ad1c581e GM |
706 | if (count) |
707 | ring_consumed(&ttyiring, count); | |
d5c9ed55 | 708 | return returnValue||count; /* Non-zero if we did anything */ |
40e8c2a3 GM |
709 | } |
710 | \f | |
890e6cf8 GM |
711 | /* |
712 | * Scheduler() | |
713 | * | |
714 | * Try to do something. | |
715 | * | |
716 | * If we do something useful, return 1; else return 0. | |
717 | * | |
718 | */ | |
40e8c2a3 | 719 | |
890e6cf8 GM |
720 | |
721 | int | |
722 | Scheduler(block) | |
723 | int block; /* should we block in the select ? */ | |
40e8c2a3 | 724 | { |
890e6cf8 GM |
725 | /* One wants to be a bit careful about setting returnValue |
726 | * to one, since a one implies we did some useful work, | |
727 | * and therefore probably won't be called to block next | |
728 | * time (TN3270 mode only). | |
729 | */ | |
b307f09e GM |
730 | int returnValue; |
731 | int netin, netout, netex, ttyin, ttyout; | |
732 | ||
733 | /* Decide which rings should be processed */ | |
734 | ||
735 | netout = ring_full_count(&netoring) && | |
736 | (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); | |
737 | ttyout = ring_full_count(&ttyoring); | |
738 | ||
890e6cf8 | 739 | #if defined(TN3270) |
b307f09e | 740 | ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); |
890e6cf8 | 741 | #else /* defined(TN3270) */ |
b307f09e | 742 | ttyin = ring_empty_count(&ttyiring); |
890e6cf8 | 743 | #endif /* defined(TN3270) */ |
b307f09e GM |
744 | |
745 | #if defined(TN3270) | |
746 | netin = ring_empty_count(&netiring); | |
890e6cf8 | 747 | # else /* !defined(TN3270) */ |
b307f09e | 748 | netin = !ISend && ring_empty_count(&netiring); |
890e6cf8 | 749 | # endif /* !defined(TN3270) */ |
b307f09e GM |
750 | |
751 | netex = !SYNCHing; | |
752 | ||
753 | /* If we have seen a signal recently, reset things */ | |
890e6cf8 GM |
754 | # if defined(TN3270) && defined(unix) |
755 | if (HaveInput) { | |
756 | HaveInput = 0; | |
757 | signal(SIGIO, inputAvailable); | |
758 | } | |
759 | #endif /* defined(TN3270) && defined(unix) */ | |
040b6435 | 760 | |
b307f09e | 761 | /* Call to system code to process rings */ |
040b6435 | 762 | |
b307f09e | 763 | returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); |
040b6435 | 764 | |
b307f09e | 765 | /* Now, look at the input rings, looking for work to do. */ |
890e6cf8 | 766 | |
8b6750f5 | 767 | if (ring_full_count(&ttyiring)) { |
b307f09e | 768 | # if defined(TN3270) |
890e6cf8 | 769 | if (In3270) { |
80a47e22 GM |
770 | int c; |
771 | ||
2e48960f | 772 | c = DataFromTerminal(ttyiring.consume, |
8b6750f5 | 773 | ring_full_consecutive(&ttyiring)); |
890e6cf8 GM |
774 | if (c) { |
775 | returnValue = 1; | |
ad1c581e | 776 | ring_consumed(&ttyiring, c); |
890e6cf8 | 777 | } |
890e6cf8 GM |
778 | } else { |
779 | # endif /* defined(TN3270) */ | |
f8362f44 | 780 | returnValue |= telsnd(); |
890e6cf8 | 781 | # if defined(TN3270) |
040b6435 | 782 | } |
890e6cf8 | 783 | # endif /* defined(TN3270) */ |
890e6cf8 | 784 | } |
b307f09e | 785 | |
8b6750f5 | 786 | if (ring_full_count(&netiring)) { |
890e6cf8 | 787 | # if !defined(TN3270) |
d5c9ed55 | 788 | returnValue |= telrcv(); |
890e6cf8 GM |
789 | # else /* !defined(TN3270) */ |
790 | returnValue = Push3270(); | |
791 | # endif /* !defined(TN3270) */ | |
792 | } | |
890e6cf8 | 793 | return returnValue; |
040b6435 GM |
794 | } |
795 | \f | |
3e16c811 | 796 | /* |
890e6cf8 | 797 | * Select from tty and network... |
3e16c811 | 798 | */ |
890e6cf8 GM |
799 | void |
800 | telnet() | |
3e16c811 | 801 | { |
b307f09e | 802 | sys_telnet_init(); |
3e16c811 | 803 | |
890e6cf8 GM |
804 | # if !defined(TN3270) |
805 | if (telnetport) { | |
806 | if (!hisopts[TELOPT_SGA]) { | |
807 | willoption(TELOPT_SGA, 0); | |
3e16c811 | 808 | } |
890e6cf8 | 809 | if (!myopts[TELOPT_TTYPE]) { |
80a47e22 | 810 | dooption(TELOPT_TTYPE); |
40e8c2a3 | 811 | } |
040b6435 | 812 | } |
890e6cf8 | 813 | # endif /* !defined(TN3270) */ |
3e16c811 | 814 | |
890e6cf8 GM |
815 | # if !defined(TN3270) |
816 | for (;;) { | |
d5c9ed55 GM |
817 | int schedValue; |
818 | ||
819 | while ((schedValue = Scheduler(0)) != 0) { | |
820 | if (schedValue == -1) { | |
821 | setcommandmode(); | |
822 | return; | |
823 | } | |
824 | } | |
825 | ||
b307f09e | 826 | if (Scheduler(1) == -1) { |
890e6cf8 GM |
827 | setcommandmode(); |
828 | return; | |
3e16c811 | 829 | } |
890e6cf8 GM |
830 | } |
831 | # else /* !defined(TN3270) */ | |
832 | for (;;) { | |
833 | int schedValue; | |
834 | ||
835 | while (!In3270 && !shell_active) { | |
b307f09e | 836 | if (Scheduler(1) == -1) { |
890e6cf8 GM |
837 | setcommandmode(); |
838 | return; | |
839 | } | |
3e16c811 | 840 | } |
890e6cf8 GM |
841 | |
842 | while ((schedValue = Scheduler(0)) != 0) { | |
843 | if (schedValue == -1) { | |
844 | setcommandmode(); | |
845 | return; | |
846 | } | |
3e16c811 | 847 | } |
890e6cf8 GM |
848 | /* If there is data waiting to go out to terminal, don't |
849 | * schedule any more data for the terminal. | |
850 | */ | |
eb1b2833 | 851 | if (ring_full_count(&ttyoring)) { |
890e6cf8 | 852 | schedValue = 1; |
3e16c811 | 853 | } else { |
890e6cf8 GM |
854 | if (shell_active) { |
855 | if (shell_continue() == 0) { | |
856 | ConnectScreen(); | |
3e16c811 | 857 | } |
890e6cf8 GM |
858 | } else if (In3270) { |
859 | schedValue = DoTerminalOutput(); | |
860 | } | |
3e16c811 | 861 | } |
890e6cf8 | 862 | if (schedValue && (shell_active == 0)) { |
b307f09e | 863 | if (Scheduler(1) == -1) { |
890e6cf8 GM |
864 | setcommandmode(); |
865 | return; | |
866 | } | |
3e16c811 | 867 | } |
890e6cf8 GM |
868 | } |
869 | # endif /* !defined(TN3270) */ | |
3e16c811 | 870 | } |
890e6cf8 | 871 | \f |
80a47e22 | 872 | #if 0 /* XXX - this not being in is a bug */ |
f8362f44 GM |
873 | /* |
874 | * nextitem() | |
875 | * | |
876 | * Return the address of the next "item" in the TELNET data | |
877 | * stream. This will be the address of the next character if | |
878 | * the current address is a user data character, or it will | |
879 | * be the address of the character following the TELNET command | |
880 | * if the current address is a TELNET IAC ("I Am a Command") | |
881 | * character. | |
882 | */ | |
883 | ||
884 | static char * | |
885 | nextitem(current) | |
886 | char *current; | |
887 | { | |
888 | if ((*current&0xff) != IAC) { | |
889 | return current+1; | |
890 | } | |
891 | switch (*(current+1)&0xff) { | |
892 | case DO: | |
893 | case DONT: | |
894 | case WILL: | |
895 | case WONT: | |
896 | return current+3; | |
897 | case SB: /* loop forever looking for the SE */ | |
898 | { | |
899 | register char *look = current+2; | |
900 | ||
901 | for (;;) { | |
902 | if ((*look++&0xff) == IAC) { | |
903 | if ((*look++&0xff) == SE) { | |
904 | return look; | |
905 | } | |
906 | } | |
907 | } | |
908 | } | |
909 | default: | |
910 | return current+2; | |
911 | } | |
912 | } | |
80a47e22 | 913 | #endif /* 0 */ |
f8362f44 GM |
914 | |
915 | /* | |
916 | * netclear() | |
917 | * | |
918 | * We are about to do a TELNET SYNCH operation. Clear | |
919 | * the path to the network. | |
920 | * | |
921 | * Things are a bit tricky since we may have sent the first | |
922 | * byte or so of a previous TELNET command into the network. | |
923 | * So, we have to scan the network buffer from the beginning | |
924 | * until we are up to where we want to be. | |
925 | * | |
926 | * A side effect of what we do, just to keep things | |
927 | * simple, is to clear the urgent data pointer. The principal | |
928 | * caller should be setting the urgent data pointer AFTER calling | |
929 | * us in any case. | |
930 | */ | |
931 | ||
932 | static void | |
933 | netclear() | |
934 | { | |
935 | #if 0 /* XXX */ | |
936 | register char *thisitem, *next; | |
937 | char *good; | |
938 | #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ | |
939 | ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) | |
940 | ||
941 | thisitem = netobuf; | |
942 | ||
943 | while ((next = nextitem(thisitem)) <= netobuf.send) { | |
944 | thisitem = next; | |
945 | } | |
946 | ||
947 | /* Now, thisitem is first before/at boundary. */ | |
948 | ||
949 | good = netobuf; /* where the good bytes go */ | |
950 | ||
951 | while (netoring.add > thisitem) { | |
952 | if (wewant(thisitem)) { | |
953 | int length; | |
954 | ||
955 | next = thisitem; | |
956 | do { | |
957 | next = nextitem(next); | |
958 | } while (wewant(next) && (nfrontp > next)); | |
959 | length = next-thisitem; | |
960 | memcpy(good, thisitem, length); | |
961 | good += length; | |
962 | thisitem = next; | |
963 | } else { | |
964 | thisitem = nextitem(thisitem); | |
965 | } | |
966 | } | |
967 | ||
968 | #endif /* 0 */ | |
969 | } | |
970 | \f | |
3e16c811 | 971 | /* |
890e6cf8 | 972 | * These routines add various telnet commands to the data stream. |
3e16c811 | 973 | */ |
890e6cf8 | 974 | |
f8362f44 GM |
975 | static void |
976 | doflush() | |
977 | { | |
978 | NET2ADD(IAC, DO); | |
979 | NETADD(TELOPT_TM); | |
980 | flushline = 1; | |
981 | flushout = 1; | |
982 | ttyflush(1); /* Flush/drop output */ | |
983 | /* do printoption AFTER flush, otherwise the output gets tossed... */ | |
984 | printoption("<SENT", doopt, TELOPT_TM, 0); | |
985 | } | |
986 | ||
890e6cf8 GM |
987 | void |
988 | xmitAO() | |
3e16c811 | 989 | { |
890e6cf8 GM |
990 | NET2ADD(IAC, AO); |
991 | if (autoflush) { | |
992 | doflush(); | |
993 | } | |
3e16c811 | 994 | } |
3e16c811 | 995 | |
3e16c811 | 996 | |
890e6cf8 GM |
997 | void |
998 | xmitEL() | |
3e16c811 | 999 | { |
890e6cf8 | 1000 | NET2ADD(IAC, EL); |
3e16c811 GM |
1001 | } |
1002 | ||
890e6cf8 GM |
1003 | void |
1004 | xmitEC() | |
3e16c811 | 1005 | { |
890e6cf8 | 1006 | NET2ADD(IAC, EC); |
3e16c811 GM |
1007 | } |
1008 | ||
890e6cf8 GM |
1009 | |
1010 | #if defined(NOT43) | |
1011 | int | |
1012 | #else /* defined(NOT43) */ | |
1013 | void | |
1014 | #endif /* defined(NOT43) */ | |
1015 | dosynch() | |
3e16c811 | 1016 | { |
890e6cf8 | 1017 | netclear(); /* clear the path to the network */ |
218b1a4c GM |
1018 | NETADD(IAC); |
1019 | setneturg(); | |
1020 | NETADD(DM); | |
3087f2df | 1021 | |
890e6cf8 GM |
1022 | #if defined(NOT43) |
1023 | return 0; | |
1024 | #endif /* defined(NOT43) */ | |
3e16c811 GM |
1025 | } |
1026 | ||
890e6cf8 GM |
1027 | void |
1028 | intp() | |
1029 | { | |
1030 | NET2ADD(IAC, IP); | |
1031 | flushline = 1; | |
1032 | if (autoflush) { | |
1033 | doflush(); | |
1034 | } | |
1035 | if (autosynch) { | |
1036 | dosynch(); | |
1037 | } | |
1038 | } | |
f2bf20fe | 1039 | |
890e6cf8 GM |
1040 | void |
1041 | sendbrk() | |
f2bf20fe | 1042 | { |
890e6cf8 GM |
1043 | NET2ADD(IAC, BREAK); |
1044 | flushline = 1; | |
1045 | if (autoflush) { | |
1046 | doflush(); | |
1047 | } | |
1048 | if (autosynch) { | |
1049 | dosynch(); | |
1050 | } | |
f2bf20fe | 1051 | } |