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