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) |
4ad36dea GM |
78 | noasynchtty = 0,/* User specified "-noasynch" on command line */ |
79 | noasynchnet = 0,/* User specified "-noasynch" on command line */ | |
890e6cf8 | 80 | askedSGA = 0, /* We have talked about suppress go ahead */ |
80a47e22 | 81 | #endif /* defined(TN3270) */ |
448f9c06 | 82 | telnetport, |
b307f09e GM |
83 | SYNCHing, /* we are in TELNET SYNCH mode */ |
84 | flushout, /* flush output */ | |
85 | autoflush = 0, /* flush output when interrupting? */ | |
86 | autosynch, /* send interrupt characters with SYNCH? */ | |
87 | localchars, /* we recognize interrupt/quit */ | |
88 | donelclchars, /* the user has set "localchars" */ | |
89 | donebinarytoggle, /* the user has put us in binary */ | |
90 | dontlecho, /* do we suppress local echoing right now? */ | |
91 | globalmode; | |
890e6cf8 GM |
92 | |
93 | #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ | |
94 | ||
95 | char | |
96 | *prompt = 0, | |
97 | escape, | |
98 | echoc; | |
99 | ||
890e6cf8 GM |
100 | /* |
101 | * Telnet receiver states for fsm | |
102 | */ | |
103 | #define TS_DATA 0 | |
104 | #define TS_IAC 1 | |
105 | #define TS_WILL 2 | |
106 | #define TS_WONT 3 | |
107 | #define TS_DO 4 | |
108 | #define TS_DONT 5 | |
109 | #define TS_CR 6 | |
110 | #define TS_SB 7 /* sub-option collection */ | |
111 | #define TS_SE 8 /* looking for sub-option end */ | |
a19db822 | 112 | |
890e6cf8 | 113 | static int telrcv_state; |
a19db822 | 114 | |
890e6cf8 GM |
115 | jmp_buf toplevel = { 0 }; |
116 | jmp_buf peerdied; | |
40e8c2a3 | 117 | |
890e6cf8 | 118 | int flushline; |
40e8c2a3 GM |
119 | |
120 | /* | |
121 | * The following are some clocks used to decide how to interpret | |
040b6435 | 122 | * the relationship between various variables. |
40e8c2a3 GM |
123 | */ |
124 | ||
890e6cf8 GM |
125 | Clocks clocks; |
126 | \f | |
127 | Modelist modelist[] = { | |
128 | { "telnet command mode", COMMAND_LINE }, | |
129 | { "character-at-a-time mode", 0 }, | |
130 | { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, | |
131 | { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, | |
132 | { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, | |
133 | { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, | |
134 | { "3270 mode", 0 }, | |
135 | }; | |
40e8c2a3 | 136 | |
40e8c2a3 GM |
137 | \f |
138 | /* | |
890e6cf8 | 139 | * Initialize telnet environment. |
40e8c2a3 | 140 | */ |
3e16c811 | 141 | |
890e6cf8 GM |
142 | init_telnet() |
143 | { | |
890e6cf8 GM |
144 | SB_CLEAR(); |
145 | ClearArray(hisopts); | |
146 | ClearArray(myopts); | |
f2bf20fe | 147 | |
f8362f44 | 148 | connected = In3270 = ISend = donebinarytoggle = 0; |
f2bf20fe | 149 | |
890e6cf8 GM |
150 | SYNCHing = 0; |
151 | ||
890e6cf8 GM |
152 | /* Don't change NetTrace */ |
153 | ||
154 | escape = CONTROL(']'); | |
155 | echoc = CONTROL('E'); | |
156 | ||
157 | flushline = 1; | |
158 | telrcv_state = TS_DATA; | |
a19db822 | 159 | } |
f8362f44 GM |
160 | \f |
161 | ||
162 | #include <varargs.h> | |
163 | ||
80a47e22 | 164 | /*VARARGS*/ |
f8362f44 GM |
165 | static void |
166 | printring(va_alist) | |
167 | va_dcl | |
168 | { | |
169 | va_list ap; | |
170 | char buffer[100]; /* where things go */ | |
171 | char *ptr; | |
172 | char *format; | |
173 | char *string; | |
174 | Ring *ring; | |
175 | int i; | |
176 | ||
177 | va_start(ap); | |
178 | ||
179 | ring = va_arg(ap, Ring *); | |
180 | format = va_arg(ap, char *); | |
181 | ptr = buffer; | |
182 | ||
183 | while ((i = *format++) != 0) { | |
184 | if (i == '%') { | |
185 | i = *format++; | |
186 | switch (i) { | |
187 | case 'c': | |
188 | *ptr++ = va_arg(ap, int); | |
189 | break; | |
190 | case 's': | |
191 | string = va_arg(ap, char *); | |
192 | ring_supply_data(ring, buffer, ptr-buffer); | |
193 | ring_supply_data(ring, string, strlen(string)); | |
194 | ptr = buffer; | |
195 | break; | |
196 | case 0: | |
197 | ExitString("printring: trailing %%.\n", 1); | |
198 | /*NOTREACHED*/ | |
199 | default: | |
200 | ExitString("printring: unknown format character.\n", 1); | |
201 | /*NOTREACHED*/ | |
202 | } | |
203 | } else { | |
204 | *ptr++ = i; | |
205 | } | |
206 | } | |
207 | ring_supply_data(ring, buffer, ptr-buffer); | |
208 | } | |
a19db822 | 209 | |
40e8c2a3 | 210 | |
890e6cf8 GM |
211 | void |
212 | willoption(option, reply) | |
213 | int option, reply; | |
40e8c2a3 | 214 | { |
890e6cf8 GM |
215 | char *fmt; |
216 | ||
217 | switch (option) { | |
218 | ||
219 | case TELOPT_ECHO: | |
220 | # if defined(TN3270) | |
221 | /* | |
222 | * The following is a pain in the rear-end. | |
223 | * Various IBM servers (some versions of Wiscnet, | |
224 | * possibly Fibronics/Spartacus, and who knows who | |
225 | * else) will NOT allow us to send "DO SGA" too early | |
226 | * in the setup proceedings. On the other hand, | |
227 | * 4.2 servers (telnetd) won't set SGA correctly. | |
228 | * So, we are stuck. Empirically (but, based on | |
229 | * a VERY small sample), the IBM servers don't send | |
230 | * out anything about ECHO, so we postpone our sending | |
231 | * "DO SGA" until we see "WILL ECHO" (which 4.2 servers | |
232 | * DO send). | |
233 | */ | |
234 | { | |
235 | if (askedSGA == 0) { | |
236 | askedSGA = 1; | |
237 | if (!hisopts[TELOPT_SGA]) { | |
238 | willoption(TELOPT_SGA, 0); | |
239 | } | |
40e8c2a3 | 240 | } |
890e6cf8 GM |
241 | } |
242 | /* Fall through */ | |
243 | case TELOPT_EOR: | |
244 | case TELOPT_BINARY: | |
245 | #endif /* defined(TN3270) */ | |
246 | case TELOPT_SGA: | |
247 | settimer(modenegotiated); | |
248 | hisopts[option] = 1; | |
249 | fmt = doopt; | |
250 | setconnmode(); /* possibly set new tty mode */ | |
40e8c2a3 | 251 | break; |
890e6cf8 GM |
252 | |
253 | case TELOPT_TM: | |
254 | return; /* Never reply to TM will's/wont's */ | |
255 | ||
40e8c2a3 | 256 | default: |
890e6cf8 | 257 | fmt = dont; |
40e8c2a3 GM |
258 | break; |
259 | } | |
f8362f44 | 260 | printring(&netoring, fmt, option); |
890e6cf8 GM |
261 | if (reply) |
262 | printoption(">SENT", fmt, option, reply); | |
263 | else | |
264 | printoption("<SENT", fmt, option, reply); | |
40e8c2a3 | 265 | } |
f2bf20fe | 266 | |
890e6cf8 GM |
267 | void |
268 | wontoption(option, reply) | |
269 | int option, reply; | |
f2bf20fe | 270 | { |
890e6cf8 | 271 | char *fmt; |
0ea293c6 | 272 | |
890e6cf8 | 273 | switch (option) { |
0ea293c6 | 274 | |
890e6cf8 GM |
275 | case TELOPT_ECHO: |
276 | case TELOPT_SGA: | |
277 | settimer(modenegotiated); | |
278 | hisopts[option] = 0; | |
279 | fmt = dont; | |
280 | setconnmode(); /* Set new tty mode */ | |
281 | break; | |
0ea293c6 | 282 | |
890e6cf8 GM |
283 | case TELOPT_TM: |
284 | return; /* Never reply to TM will's/wont's */ | |
0ea293c6 | 285 | |
890e6cf8 GM |
286 | default: |
287 | fmt = dont; | |
0ea293c6 | 288 | } |
f8362f44 | 289 | printring(&netoring, fmt, option); |
890e6cf8 GM |
290 | if (reply) |
291 | printoption(">SENT", fmt, option, reply); | |
292 | else | |
293 | printoption("<SENT", fmt, option, reply); | |
0ea293c6 | 294 | } |
f2bf20fe | 295 | |
890e6cf8 GM |
296 | static void |
297 | dooption(option) | |
298 | int option; | |
f2bf20fe | 299 | { |
890e6cf8 | 300 | char *fmt; |
f2bf20fe | 301 | |
890e6cf8 | 302 | switch (option) { |
f2bf20fe | 303 | |
890e6cf8 GM |
304 | case TELOPT_TM: |
305 | fmt = will; | |
306 | break; | |
f2bf20fe | 307 | |
890e6cf8 GM |
308 | # if defined(TN3270) |
309 | case TELOPT_EOR: | |
310 | case TELOPT_BINARY: | |
311 | # endif /* defined(TN3270) */ | |
312 | case TELOPT_TTYPE: /* terminal type option */ | |
313 | case TELOPT_SGA: /* no big deal */ | |
314 | fmt = will; | |
315 | myopts[option] = 1; | |
316 | break; | |
f2bf20fe | 317 | |
890e6cf8 GM |
318 | case TELOPT_ECHO: /* We're never going to echo... */ |
319 | default: | |
320 | fmt = wont; | |
321 | break; | |
f2bf20fe | 322 | } |
f8362f44 | 323 | printring(&netoring, fmt, option); |
890e6cf8 | 324 | printoption(">SENT", fmt, option, 0); |
f2bf20fe | 325 | } |
3087f2df | 326 | |
3087f2df | 327 | /* |
890e6cf8 | 328 | * suboption() |
3087f2df | 329 | * |
890e6cf8 GM |
330 | * Look at the sub-option buffer, and try to be helpful to the other |
331 | * side. | |
3087f2df | 332 | * |
890e6cf8 | 333 | * Currently we recognize: |
3087f2df | 334 | * |
890e6cf8 | 335 | * Terminal type, send request. |
3087f2df GM |
336 | */ |
337 | ||
890e6cf8 GM |
338 | static void |
339 | suboption() | |
3087f2df | 340 | { |
890e6cf8 GM |
341 | printsub("<", subbuffer, subend-subbuffer+1); |
342 | switch (subbuffer[0]&0xff) { | |
343 | case TELOPT_TTYPE: | |
344 | if ((subbuffer[1]&0xff) != TELQUAL_SEND) { | |
345 | ; | |
3087f2df | 346 | } else { |
890e6cf8 GM |
347 | char *name; |
348 | char namebuf[41]; | |
349 | extern char *getenv(); | |
350 | int len; | |
f2bf20fe | 351 | |
890e6cf8 | 352 | #if defined(TN3270) |
b307f09e | 353 | if (tn3270_ttype()) { |
890e6cf8 GM |
354 | return; |
355 | } | |
356 | #endif /* defined(TN3270) */ | |
890e6cf8 GM |
357 | name = getenv("TERM"); |
358 | if ((name == 0) || ((len = strlen(name)) > 40)) { | |
359 | name = "UNKNOWN"; | |
a044ddbc | 360 | len = strlen(name); |
890e6cf8 GM |
361 | } |
362 | if ((len + 4+2) < NETROOM()) { | |
363 | strcpy(namebuf, name); | |
364 | upcase(namebuf); | |
f8362f44 | 365 | printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, |
890e6cf8 | 366 | TELQUAL_IS, namebuf, IAC, SE); |
115a5494 GM |
367 | /* XXX */ |
368 | /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ | |
890e6cf8 | 369 | } else { |
115a5494 | 370 | ExitString("No room in buffer for terminal type.\n", |
890e6cf8 GM |
371 | 1); |
372 | /*NOTREACHED*/ | |
373 | } | |
f2bf20fe | 374 | } |
40e8c2a3 | 375 | |
890e6cf8 GM |
376 | default: |
377 | break; | |
40e8c2a3 | 378 | } |
40e8c2a3 | 379 | } |
40e8c2a3 | 380 | \f |
f2bf20fe | 381 | |
2e48960f | 382 | int |
890e6cf8 | 383 | telrcv() |
f2bf20fe | 384 | { |
890e6cf8 | 385 | register int c; |
d5c9ed55 GM |
386 | register int scc; |
387 | register char *sbp; | |
388 | int count; | |
389 | int returnValue = 0; | |
390 | ||
391 | scc = 0; | |
392 | count = 0; | |
393 | while (TTYROOM() > 2) { | |
394 | if (scc == 0) { | |
395 | if (count) { | |
8b6750f5 | 396 | ring_consumed(&netiring, count); |
d5c9ed55 GM |
397 | returnValue = 1; |
398 | count = 0; | |
399 | } | |
8b6750f5 GM |
400 | sbp = netiring.consume; |
401 | scc = ring_full_consecutive(&netiring); | |
d5c9ed55 GM |
402 | if (scc == 0) { |
403 | /* No more data coming in */ | |
404 | break; | |
405 | } | |
406 | } | |
407 | ||
408 | c = *sbp++ & 0xff, scc--; count++; | |
890e6cf8 | 409 | |
890e6cf8 GM |
410 | switch (telrcv_state) { |
411 | ||
412 | case TS_CR: | |
413 | telrcv_state = TS_DATA; | |
811c5ce5 GM |
414 | if (c == '\0') { |
415 | break; /* Ignore \0 after CR */ | |
416 | } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) { | |
417 | TTYADD(c); | |
418 | break; | |
0ea293c6 | 419 | } |
811c5ce5 | 420 | /* Else, fall through */ |
890e6cf8 GM |
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) */ | |
811c5ce5 GM |
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 | */ | |
890e6cf8 | 447 | if ((c == '\r') && !hisopts[TELOPT_BINARY]) { |
811c5ce5 GM |
448 | if (scc > 0) { |
449 | c = *sbp&0xff; | |
450 | if (c == 0) { | |
451 | sbp++, scc--; count++; | |
452 | /* a "true" CR */ | |
890e6cf8 | 453 | TTYADD('\r'); |
811c5ce5 GM |
454 | } else if (!hisopts[TELOPT_ECHO] && |
455 | (c == '\n')) { | |
456 | sbp++, scc--; count++; | |
890e6cf8 | 457 | TTYADD('\n'); |
811c5ce5 GM |
458 | } else { |
459 | TTYADD('\r'); | |
460 | if (crmod) { | |
461 | TTYADD('\n'); | |
890e6cf8 | 462 | } |
811c5ce5 GM |
463 | } |
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 | } |