1 | /* |
---|
2 | * Modifications Copyright 1993, 1994, 1995 by Paul Mattes. |
---|
3 | * Original X11 Port Copyright 1990 by Jeff Sparkes. |
---|
4 | * Permission to use, copy, modify, and distribute this software and its |
---|
5 | * documentation for any purpose and without fee is hereby granted, |
---|
6 | * provided that the above copyright notice appear in all copies and that |
---|
7 | * both that copyright notice and this permission notice appear in |
---|
8 | * supporting documentation. |
---|
9 | * |
---|
10 | * Copyright 1989 by Georgia Tech Research Corporation, Atlanta, GA 30332. |
---|
11 | * All Rights Reserved. GTRC hereby grants public use of this software. |
---|
12 | * Derivative works based on this software must incorporate this copyright |
---|
13 | * notice. |
---|
14 | */ |
---|
15 | |
---|
16 | /* |
---|
17 | * telnet.c |
---|
18 | * This module initializes and manages a telnet socket to |
---|
19 | * the given IBM host. |
---|
20 | */ |
---|
21 | |
---|
22 | #include "globals.h" |
---|
23 | #include <sys/socket.h> |
---|
24 | #include <sys/ioctl.h> |
---|
25 | #include <netinet/in.h> |
---|
26 | #define TELCMDS 1 |
---|
27 | #define TELOPTS 1 |
---|
28 | #if !defined(LOCAL_TELNET_H) /*[*/ |
---|
29 | #include <arpa/telnet.h> |
---|
30 | #else /*][*/ |
---|
31 | #include "telnet.h" |
---|
32 | #endif /*]*/ |
---|
33 | #include <errno.h> |
---|
34 | #include <fcntl.h> |
---|
35 | #include <netdb.h> |
---|
36 | |
---|
37 | #include "appres.h" |
---|
38 | |
---|
39 | #include "ansic.h" |
---|
40 | #include "ctlrc.h" |
---|
41 | #include "kybdc.h" |
---|
42 | #include "macrosc.h" |
---|
43 | #include "mainc.h" |
---|
44 | #include "menubarc.h" |
---|
45 | #include "popupsc.h" |
---|
46 | #include "statusc.h" |
---|
47 | #include "telnetc.h" |
---|
48 | #include "trace_dsc.h" |
---|
49 | #include "utilc.h" |
---|
50 | |
---|
51 | #if !defined(TELOPT_NAWS) /*[*/ |
---|
52 | #define TELOPT_NAWS 31 |
---|
53 | #endif /*]*/ |
---|
54 | |
---|
55 | #if !defined(TELOPT_TN3270E) /*[*/ |
---|
56 | #define TELOPT_TN3270E 40 |
---|
57 | #endif /*]*/ |
---|
58 | |
---|
59 | #define BUFSZ 4096 |
---|
60 | #define TRACELINE 72 |
---|
61 | |
---|
62 | #define N_OPTS 256 |
---|
63 | |
---|
64 | /* Globals */ |
---|
65 | time_t ns_time; |
---|
66 | int ns_brcvd; |
---|
67 | int ns_rrcvd; |
---|
68 | int ns_bsent; |
---|
69 | int ns_rsent; |
---|
70 | unsigned char *obuf; /* 3270 output buffer */ |
---|
71 | int obuf_size = 0; |
---|
72 | unsigned char *obptr = (unsigned char *) NULL; |
---|
73 | int linemode = 1; |
---|
74 | char *termtype; |
---|
75 | void trace_netdata(); |
---|
76 | |
---|
77 | /* Externals */ |
---|
78 | extern unsigned long inet_addr(); |
---|
79 | extern FILE *tracef; |
---|
80 | extern struct timeval ds_ts; |
---|
81 | |
---|
82 | /* Statics */ |
---|
83 | static int sock = -1; /* active socket */ |
---|
84 | static char *hostname = CN; |
---|
85 | static unsigned char myopts[N_OPTS], hisopts[N_OPTS]; /* telnet option flags */ |
---|
86 | static unsigned char *ibuf = (unsigned char *) NULL; /* 3270 input buffer */ |
---|
87 | static unsigned char *ibptr; |
---|
88 | static int ibuf_size = 0; /* size of ibuf */ |
---|
89 | static unsigned char netrbuf[BUFSZ]; /* network input buffer */ |
---|
90 | static unsigned char sbbuf[1024], *sbptr; /* telnet sub-option buffer */ |
---|
91 | static unsigned char lbuf[BUFSZ], *lbptr; /* line-mode input buffer */ |
---|
92 | static unsigned char telnet_state; |
---|
93 | static char trace_msg[256]; |
---|
94 | static int ansi_data = 0; |
---|
95 | static int syncing; |
---|
96 | static int lnext = 0; |
---|
97 | static int backslashed = 0; |
---|
98 | static int t_valid = 0; |
---|
99 | static char ttype_tmpval[13]; |
---|
100 | |
---|
101 | static char vintr; |
---|
102 | static char vquit; |
---|
103 | static char verase; |
---|
104 | static char vkill; |
---|
105 | static char veof; |
---|
106 | static char vwerase; |
---|
107 | static char vrprnt; |
---|
108 | static char vlnext; |
---|
109 | |
---|
110 | static char *nnn(); /* expand a number */ |
---|
111 | static int telnet_fsm(); |
---|
112 | static void net_rawout(); |
---|
113 | static void do_data(); |
---|
114 | static void do_intr(); |
---|
115 | static void do_quit(); |
---|
116 | static void do_cerase(); |
---|
117 | static void do_werase(); |
---|
118 | static void do_kill(); |
---|
119 | static void do_rprnt(); |
---|
120 | static void do_eof(); |
---|
121 | static void do_eol(); |
---|
122 | static void do_lnext(); |
---|
123 | static void check_in3270(); |
---|
124 | static void store3270in(); |
---|
125 | static void check_linemode(); |
---|
126 | static char parse_ctlchar(); |
---|
127 | static void net_interrupt(); |
---|
128 | static int non_blocking(); |
---|
129 | static void net_connected(); |
---|
130 | static void trace_str(); |
---|
131 | static char *cmd(); |
---|
132 | static char *opt(); |
---|
133 | |
---|
134 | /* telnet states */ |
---|
135 | #define TNS_DATA 0 /* receiving data */ |
---|
136 | #define TNS_IAC 1 /* got an IAC */ |
---|
137 | #define TNS_WILL 2 /* got an IAC WILL */ |
---|
138 | #define TNS_WONT 3 /* got an IAC WONT */ |
---|
139 | #define TNS_DO 4 /* got an IAC DO */ |
---|
140 | #define TNS_DONT 5 /* got an IAC DONT */ |
---|
141 | #define TNS_SB 6 /* got an IAC SB */ |
---|
142 | #define TNS_SB_IAC 7 /* got an IAC after an IAC SB */ |
---|
143 | |
---|
144 | /* telnet predefined messages */ |
---|
145 | static unsigned char do_opt[] = { |
---|
146 | IAC, DO, '_' }; |
---|
147 | static unsigned char dont_opt[] = { |
---|
148 | IAC, DONT, '_' }; |
---|
149 | static unsigned char will_opt[] = { |
---|
150 | IAC, WILL, '_' }; |
---|
151 | static unsigned char wont_opt[] = { |
---|
152 | IAC, WONT, '_' }; |
---|
153 | |
---|
154 | char *telquals[2] = { "IS", "SEND" }; |
---|
155 | |
---|
156 | |
---|
157 | /* |
---|
158 | * net_connect |
---|
159 | * Establish a telnet socket to the given host passed as an argument. |
---|
160 | * Called only once and is responsible for setting up the telnet |
---|
161 | * variables. Returns the file descriptor of the connected socket. |
---|
162 | */ |
---|
163 | int |
---|
164 | net_connect(host, portname, pending) |
---|
165 | char *host; |
---|
166 | char *portname; |
---|
167 | Boolean *pending; |
---|
168 | { |
---|
169 | struct servent *sp; |
---|
170 | struct hostent *hp; |
---|
171 | unsigned short port; |
---|
172 | char passthru_haddr[8]; |
---|
173 | int passthru_len; |
---|
174 | unsigned short passthru_port; |
---|
175 | struct sockaddr_in sin; |
---|
176 | int on = 1; |
---|
177 | #if defined(OMTU) /*[*/ |
---|
178 | int mtu = OMTU; |
---|
179 | #endif /*]*/ |
---|
180 | |
---|
181 | # define close_fail { (void) close(sock); sock = -1; return -1; } |
---|
182 | |
---|
183 | if (!t_valid) { |
---|
184 | vintr = parse_ctlchar(appres.intr); |
---|
185 | vquit = parse_ctlchar(appres.quit); |
---|
186 | verase = parse_ctlchar(appres.erase); |
---|
187 | vkill = parse_ctlchar(appres.kill); |
---|
188 | veof = parse_ctlchar(appres.eof); |
---|
189 | vwerase = parse_ctlchar(appres.werase); |
---|
190 | vrprnt = parse_ctlchar(appres.rprnt); |
---|
191 | vlnext = parse_ctlchar(appres.lnext); |
---|
192 | t_valid = 1; |
---|
193 | } |
---|
194 | |
---|
195 | *pending = False; |
---|
196 | |
---|
197 | if (hostname) |
---|
198 | XtFree(hostname); |
---|
199 | hostname = XtNewString(host); |
---|
200 | |
---|
201 | /* get the passthru host and port number */ |
---|
202 | if (passthru_host) { |
---|
203 | char *hn; |
---|
204 | |
---|
205 | hn = getenv("INTERNET_HOST"); |
---|
206 | if (hn == CN) |
---|
207 | hn = "internet-gateway"; |
---|
208 | |
---|
209 | hp = gethostbyname(hn); |
---|
210 | if (hp == (struct hostent *) 0) { |
---|
211 | popup_an_error("Unknown passthru host: %s", hn); |
---|
212 | return -1; |
---|
213 | } |
---|
214 | (void) MEMORY_MOVE(passthru_haddr, |
---|
215 | (char *) hp->h_addr, |
---|
216 | hp->h_length); |
---|
217 | passthru_len = hp->h_length; |
---|
218 | |
---|
219 | sp = getservbyname("telnet-passthru","tcp"); |
---|
220 | if (sp != (struct servent *)NULL) |
---|
221 | passthru_port = sp->s_port; |
---|
222 | else |
---|
223 | passthru_port = htons(3514); |
---|
224 | } |
---|
225 | |
---|
226 | /* get the port number */ |
---|
227 | port = (unsigned short) atoi(portname); |
---|
228 | if (port) |
---|
229 | port = htons(port); |
---|
230 | else { |
---|
231 | if (!(sp = getservbyname(portname, "tcp"))) { |
---|
232 | popup_an_error("Unknown port number or service: %s", |
---|
233 | portname); |
---|
234 | return -1; |
---|
235 | } |
---|
236 | port = sp->s_port; |
---|
237 | } |
---|
238 | current_port = ntohs(port); |
---|
239 | |
---|
240 | |
---|
241 | /* fill in the socket address of the given host */ |
---|
242 | (void) memset((char *) &sin, 0, sizeof(sin)); |
---|
243 | if (passthru_host) { |
---|
244 | sin.sin_family = AF_INET; |
---|
245 | (void) MEMORY_MOVE((char *) &sin.sin_addr, |
---|
246 | passthru_haddr, |
---|
247 | passthru_len); |
---|
248 | sin.sin_port = passthru_port; |
---|
249 | } else { |
---|
250 | if ((hp = gethostbyname(host)) == (struct hostent *) 0) { |
---|
251 | sin.sin_family = AF_INET; |
---|
252 | sin.sin_addr.s_addr = inet_addr(host); |
---|
253 | if (sin.sin_addr.s_addr == -1) { |
---|
254 | popup_an_error("Unknown host:\n%s", hostname); |
---|
255 | return -1; |
---|
256 | } |
---|
257 | } |
---|
258 | else { |
---|
259 | sin.sin_family = hp->h_addrtype; |
---|
260 | (void) MEMORY_MOVE((char *) &sin.sin_addr, |
---|
261 | (char *) hp->h_addr, |
---|
262 | hp->h_length); |
---|
263 | } |
---|
264 | sin.sin_port = port; |
---|
265 | } |
---|
266 | |
---|
267 | /* create the socket */ |
---|
268 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
---|
269 | popup_an_errno(errno, "socket"); |
---|
270 | return -1; |
---|
271 | } |
---|
272 | |
---|
273 | /* set options for inline out-of-band data and keepalives */ |
---|
274 | if (setsockopt(sock, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { |
---|
275 | popup_an_errno(errno, "setsockopt(SO_OOBINLINE)"); |
---|
276 | close_fail; |
---|
277 | } |
---|
278 | if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) { |
---|
279 | popup_an_errno(errno, "setsockopt(SO_KEEPALIVE)"); |
---|
280 | close_fail; |
---|
281 | } |
---|
282 | #if defined(OMTU) /*[*/ |
---|
283 | if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&mtu, sizeof(mtu)) < 0) { |
---|
284 | popup_an_errno(errno, "setsockopt(SO_SNDBUF)"); |
---|
285 | close_fail; |
---|
286 | } |
---|
287 | #endif /*]*/ |
---|
288 | |
---|
289 | /* set the socket to be non-delaying */ |
---|
290 | if (non_blocking(True) < 0) |
---|
291 | close_fail; |
---|
292 | |
---|
293 | /* don't share the socket with our children */ |
---|
294 | (void) fcntl(sock, F_SETFD, 1); |
---|
295 | |
---|
296 | /* connect */ |
---|
297 | if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) { |
---|
298 | if (errno == EWOULDBLOCK |
---|
299 | #if defined(EINPROGRESS) /*[*/ |
---|
300 | || errno == EINPROGRESS |
---|
301 | #endif /*]*/ |
---|
302 | ) { |
---|
303 | trace_str("Connection pending.\n"); |
---|
304 | *pending = True; |
---|
305 | } else { |
---|
306 | popup_an_errno(errno, "Connect to %s, port %d", |
---|
307 | hostname, current_port); |
---|
308 | close_fail; |
---|
309 | } |
---|
310 | } else { |
---|
311 | if (non_blocking(False) < 0) |
---|
312 | close_fail; |
---|
313 | net_connected(); |
---|
314 | } |
---|
315 | |
---|
316 | /* set up termporary termtype */ |
---|
317 | if (appres.termname == CN && std_ds_host) { |
---|
318 | (void) sprintf(ttype_tmpval, "IBM-327%c-%d", |
---|
319 | appres.m3279 ? '9' : '8', model_num); |
---|
320 | termtype = ttype_tmpval; |
---|
321 | } |
---|
322 | |
---|
323 | /* all done */ |
---|
324 | return sock; |
---|
325 | } |
---|
326 | #undef close_fail |
---|
327 | |
---|
328 | static void |
---|
329 | net_connected() |
---|
330 | { |
---|
331 | (void) sprintf(trace_msg, "Connected to %s, port %u.\n", |
---|
332 | hostname, current_port); |
---|
333 | trace_str(trace_msg); |
---|
334 | |
---|
335 | /* set up telnet options */ |
---|
336 | (void) memset((char *) myopts, 0, sizeof(myopts)); |
---|
337 | (void) memset((char *) hisopts, 0, sizeof(hisopts)); |
---|
338 | telnet_state = TNS_DATA; |
---|
339 | ibptr = ibuf; |
---|
340 | sbptr = &sbbuf[0]; |
---|
341 | |
---|
342 | /* clear statistics and flags */ |
---|
343 | (void) time(&ns_time); |
---|
344 | ns_brcvd = 0; |
---|
345 | ns_rrcvd = 0; |
---|
346 | ns_bsent = 0; |
---|
347 | ns_rsent = 0; |
---|
348 | syncing = 0; |
---|
349 | |
---|
350 | check_linemode(True); |
---|
351 | |
---|
352 | /* write out the passthru hostname and port nubmer */ |
---|
353 | if (passthru_host) { |
---|
354 | char *buf; |
---|
355 | |
---|
356 | buf = XtMalloc(strlen(hostname) + 32); |
---|
357 | (void) sprintf(buf, "%s %d\r\n", hostname, current_port); |
---|
358 | (void) write(sock, buf, strlen(buf)); |
---|
359 | XtFree(buf); |
---|
360 | } |
---|
361 | } |
---|
362 | |
---|
363 | /* |
---|
364 | * net_disconnect |
---|
365 | * Shut down the socket. |
---|
366 | */ |
---|
367 | void |
---|
368 | net_disconnect() |
---|
369 | { |
---|
370 | if (CONNECTED) |
---|
371 | (void) shutdown(sock, 2); |
---|
372 | (void) close(sock); |
---|
373 | sock = -1; |
---|
374 | trace_str("SENT disconnect\n"); |
---|
375 | |
---|
376 | /* Restore terminal type to its default. */ |
---|
377 | if (appres.termname == CN) |
---|
378 | termtype = full_model_name; |
---|
379 | } |
---|
380 | |
---|
381 | |
---|
382 | /* |
---|
383 | * net_input |
---|
384 | * Called by the toolkit whenever there is input available on the |
---|
385 | * socket. Reads the data, processes the special telnet commands |
---|
386 | * and calls process_ds to process the 3270 data stream. |
---|
387 | */ |
---|
388 | void |
---|
389 | net_input() |
---|
390 | { |
---|
391 | register unsigned char *cp; |
---|
392 | int nr; |
---|
393 | |
---|
394 | ansi_data = 0; |
---|
395 | |
---|
396 | nr = read(sock, (char *) netrbuf, BUFSZ); |
---|
397 | if (nr < 0) { |
---|
398 | if (errno == EWOULDBLOCK) |
---|
399 | return; |
---|
400 | if (HALF_CONNECTED && errno == EAGAIN) { |
---|
401 | if (non_blocking(False) < 0) { |
---|
402 | x_disconnect(True); |
---|
403 | return; |
---|
404 | } |
---|
405 | x_connected(); |
---|
406 | net_connected(); |
---|
407 | return; |
---|
408 | } |
---|
409 | (void) sprintf(trace_msg, "RCVD socket error %d\n", errno); |
---|
410 | trace_str(trace_msg); |
---|
411 | if (HALF_CONNECTED) |
---|
412 | popup_an_errno(errno, "Connect to %s, port %d", |
---|
413 | hostname, current_port); |
---|
414 | else if (errno != ECONNRESET) |
---|
415 | popup_an_errno(errno, "Socket read"); |
---|
416 | x_disconnect(True); |
---|
417 | return; |
---|
418 | } else if (nr == 0) { |
---|
419 | /* Host disconnected. */ |
---|
420 | trace_str("RCVD disconnect\n"); |
---|
421 | x_disconnect(False); |
---|
422 | return; |
---|
423 | } |
---|
424 | |
---|
425 | /* Process the data. */ |
---|
426 | |
---|
427 | if (HALF_CONNECTED) { |
---|
428 | if (non_blocking(False) < 0) { |
---|
429 | x_disconnect(True); |
---|
430 | return; |
---|
431 | } |
---|
432 | x_connected(); |
---|
433 | net_connected(); |
---|
434 | } |
---|
435 | |
---|
436 | trace_netdata('<', netrbuf, nr); |
---|
437 | |
---|
438 | ns_brcvd += nr; |
---|
439 | for (cp = netrbuf; cp < (netrbuf + nr); cp++) |
---|
440 | if (telnet_fsm(*cp)) { |
---|
441 | x_disconnect(True); |
---|
442 | return; |
---|
443 | } |
---|
444 | |
---|
445 | if (ansi_data) { |
---|
446 | trace_str("\n"); |
---|
447 | ansi_data = 0; |
---|
448 | } |
---|
449 | } |
---|
450 | |
---|
451 | |
---|
452 | /* |
---|
453 | * set16 |
---|
454 | * Put a 16-bit value in a buffer. |
---|
455 | * Returns the number of bytes required. |
---|
456 | */ |
---|
457 | static int |
---|
458 | set16(buf, n) |
---|
459 | char *buf; |
---|
460 | int n; |
---|
461 | { |
---|
462 | char *b0 = buf; |
---|
463 | |
---|
464 | n %= 256 * 256; |
---|
465 | if ((n / 256) == IAC) |
---|
466 | *(unsigned char *)buf++ = IAC; |
---|
467 | *buf++ = (n / 256); |
---|
468 | n %= 256; |
---|
469 | if (n == IAC) |
---|
470 | *(unsigned char *)buf++ = IAC; |
---|
471 | *buf++ = n; |
---|
472 | return buf - b0; |
---|
473 | } |
---|
474 | |
---|
475 | /* |
---|
476 | * send_naws |
---|
477 | * Send a Telnet window size sub-option negotation. |
---|
478 | */ |
---|
479 | static void |
---|
480 | send_naws() |
---|
481 | { |
---|
482 | char naws_msg[14]; |
---|
483 | int naws_len = 0; |
---|
484 | |
---|
485 | (void) sprintf(naws_msg, "%c%c%c", IAC, SB, TELOPT_NAWS); |
---|
486 | naws_len += 3; |
---|
487 | naws_len += set16(naws_msg + naws_len, maxCOLS); |
---|
488 | naws_len += set16(naws_msg + naws_len, maxROWS); |
---|
489 | (void) sprintf(naws_msg + naws_len, "%c%c", IAC, SE); |
---|
490 | naws_len += 2; |
---|
491 | net_rawout((unsigned char *)naws_msg, naws_len); |
---|
492 | (void) sprintf(trace_msg, "SENT %s NAWS %d %d %s\n", cmd(SB), maxCOLS, |
---|
493 | maxROWS, cmd(SE)); |
---|
494 | trace_str(trace_msg); |
---|
495 | } |
---|
496 | |
---|
497 | |
---|
498 | /* |
---|
499 | * telnet_fsm |
---|
500 | * Telnet finite-state machine. |
---|
501 | * Returns 0 for okay, -1 for errors. |
---|
502 | */ |
---|
503 | static int |
---|
504 | telnet_fsm(c) |
---|
505 | unsigned char c; |
---|
506 | { |
---|
507 | char *see_chr; |
---|
508 | int sl; |
---|
509 | |
---|
510 | switch (telnet_state) { |
---|
511 | case TNS_DATA: /* normal data processing */ |
---|
512 | if (c == IAC) { /* got a telnet command */ |
---|
513 | telnet_state = TNS_IAC; |
---|
514 | if (ansi_data) { |
---|
515 | trace_str("\n"); |
---|
516 | ansi_data = 0; |
---|
517 | } |
---|
518 | } else if (IN_ANSI) { |
---|
519 | if (kybdlock & KL_AWAITING_FIRST) { |
---|
520 | kybdlock_clr(KL_AWAITING_FIRST, "telnet_fsm"); |
---|
521 | status_reset(); |
---|
522 | ps_process(); |
---|
523 | } |
---|
524 | if (!ansi_data) { |
---|
525 | trace_str("<.. "); |
---|
526 | ansi_data = 4; |
---|
527 | } |
---|
528 | see_chr = ctl_see((int) c); |
---|
529 | ansi_data += (sl = strlen(see_chr)); |
---|
530 | if (ansi_data >= TRACELINE) { |
---|
531 | trace_str(" ...\n... "); |
---|
532 | ansi_data = 4 + sl; |
---|
533 | } |
---|
534 | trace_str(see_chr); |
---|
535 | if (!syncing) { |
---|
536 | ansi_process((unsigned int) c); |
---|
537 | sms_store(c); |
---|
538 | } |
---|
539 | } else { |
---|
540 | store3270in(c); |
---|
541 | } |
---|
542 | break; |
---|
543 | case TNS_IAC: /* process a telnet command */ |
---|
544 | if (c != EOR && c != IAC) { |
---|
545 | (void) sprintf(trace_msg, "RCVD %s ", cmd(c)); |
---|
546 | trace_str(trace_msg); |
---|
547 | } |
---|
548 | switch (c) { |
---|
549 | case IAC: /* escaped IAC, insert it */ |
---|
550 | if (IN_ANSI) { |
---|
551 | if (!ansi_data) { |
---|
552 | trace_str("<.. "); |
---|
553 | ansi_data = 4; |
---|
554 | } |
---|
555 | see_chr = ctl_see((int) c); |
---|
556 | ansi_data += (sl = strlen(see_chr)); |
---|
557 | if (ansi_data >= TRACELINE) { |
---|
558 | trace_str(" ...\n ..."); |
---|
559 | ansi_data = 4 + sl; |
---|
560 | } |
---|
561 | trace_str(see_chr); |
---|
562 | ansi_process((unsigned int) c); |
---|
563 | sms_store(c); |
---|
564 | } else |
---|
565 | store3270in(c); |
---|
566 | telnet_state = TNS_DATA; |
---|
567 | break; |
---|
568 | case EOR: /* eor, process accumulated input */ |
---|
569 | if (IN_3270) { |
---|
570 | ns_rrcvd++; |
---|
571 | if (!syncing && (ibptr - ibuf)) |
---|
572 | if (process_ds(ibuf, ibptr - ibuf)) |
---|
573 | return -1; |
---|
574 | } else |
---|
575 | XtWarning("EOR received when not in 3270 mode, ignored."); |
---|
576 | trace_str("RCVD EOR\n"); |
---|
577 | ibptr = ibuf; |
---|
578 | telnet_state = TNS_DATA; |
---|
579 | break; |
---|
580 | case WILL: |
---|
581 | telnet_state = TNS_WILL; |
---|
582 | break; |
---|
583 | case WONT: |
---|
584 | telnet_state = TNS_WONT; |
---|
585 | break; |
---|
586 | case DO: |
---|
587 | telnet_state = TNS_DO; |
---|
588 | break; |
---|
589 | case DONT: |
---|
590 | telnet_state = TNS_DONT; |
---|
591 | break; |
---|
592 | case SB: |
---|
593 | telnet_state = TNS_SB; |
---|
594 | sbptr = sbbuf; |
---|
595 | break; |
---|
596 | case DM: |
---|
597 | trace_str("\n"); |
---|
598 | if (syncing) { |
---|
599 | syncing = 0; |
---|
600 | x_except_on(); |
---|
601 | } |
---|
602 | telnet_state = TNS_DATA; |
---|
603 | break; |
---|
604 | case GA: |
---|
605 | trace_str("\n"); |
---|
606 | telnet_state = TNS_DATA; |
---|
607 | break; |
---|
608 | default: |
---|
609 | trace_str("???\n"); |
---|
610 | telnet_state = TNS_DATA; |
---|
611 | break; |
---|
612 | } |
---|
613 | break; |
---|
614 | case TNS_WILL: /* telnet WILL DO OPTION command */ |
---|
615 | trace_str(opt(c)); |
---|
616 | trace_str("\n"); |
---|
617 | switch (c) { |
---|
618 | case TELOPT_SGA: |
---|
619 | case TELOPT_BINARY: |
---|
620 | case TELOPT_EOR: |
---|
621 | case TELOPT_TTYPE: |
---|
622 | case TELOPT_ECHO: /* hack! hack! */ |
---|
623 | if (!hisopts[c]) { |
---|
624 | hisopts[c] = 1; |
---|
625 | do_opt[2] = c; |
---|
626 | net_rawout(do_opt, sizeof(do_opt)); |
---|
627 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
628 | cmd(DO), opt(c)); |
---|
629 | trace_str(trace_msg); |
---|
630 | } |
---|
631 | check_in3270(); |
---|
632 | break; |
---|
633 | default: |
---|
634 | dont_opt[2] = c; |
---|
635 | net_rawout(dont_opt, sizeof(dont_opt)); |
---|
636 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
637 | cmd(DONT), opt(c)); |
---|
638 | trace_str(trace_msg); |
---|
639 | break; |
---|
640 | } |
---|
641 | telnet_state = TNS_DATA; |
---|
642 | check_linemode(False); |
---|
643 | break; |
---|
644 | case TNS_WONT: /* telnet WONT DO OPTION command */ |
---|
645 | trace_str(opt(c)); |
---|
646 | trace_str("\n"); |
---|
647 | switch (c) { |
---|
648 | case TELOPT_BINARY: |
---|
649 | case TELOPT_EOR: |
---|
650 | case TELOPT_TTYPE: |
---|
651 | if (hisopts[c]) { |
---|
652 | dont_opt[2] = c; |
---|
653 | net_rawout(dont_opt, sizeof(dont_opt)); |
---|
654 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
655 | cmd(DONT), opt(c)); |
---|
656 | trace_str(trace_msg); |
---|
657 | } |
---|
658 | /* fall through */ |
---|
659 | default: |
---|
660 | hisopts[c] = 0; |
---|
661 | break; |
---|
662 | } |
---|
663 | telnet_state = TNS_DATA; |
---|
664 | check_in3270(); |
---|
665 | check_linemode(False); |
---|
666 | break; |
---|
667 | case TNS_DO: /* telnet PLEASE DO OPTION command */ |
---|
668 | trace_str(opt(c)); |
---|
669 | trace_str("\n"); |
---|
670 | switch (c) { |
---|
671 | case TELOPT_BINARY: |
---|
672 | case TELOPT_EOR: |
---|
673 | case TELOPT_TTYPE: |
---|
674 | case TELOPT_SGA: |
---|
675 | case TELOPT_NAWS: |
---|
676 | case TELOPT_TM: |
---|
677 | if (!myopts[c]) { |
---|
678 | myopts[c] = 1; |
---|
679 | will_opt[2] = c; |
---|
680 | net_rawout(will_opt, sizeof(will_opt)); |
---|
681 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
682 | cmd(WILL), opt(c)); |
---|
683 | trace_str(trace_msg); |
---|
684 | } |
---|
685 | check_in3270(); |
---|
686 | if (c == TELOPT_NAWS) |
---|
687 | send_naws(); |
---|
688 | break; |
---|
689 | default: |
---|
690 | wont_opt[2] = c; |
---|
691 | net_rawout(wont_opt, sizeof(wont_opt)); |
---|
692 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
693 | cmd(WONT), opt(c)); |
---|
694 | trace_str(trace_msg); |
---|
695 | break; |
---|
696 | } |
---|
697 | telnet_state = TNS_DATA; |
---|
698 | break; |
---|
699 | case TNS_DONT: /* telnet PLEASE DON'T DO OPTION command */ |
---|
700 | trace_str(opt(c)); |
---|
701 | trace_str("\n"); |
---|
702 | switch (c) { |
---|
703 | case TELOPT_BINARY: |
---|
704 | case TELOPT_EOR: |
---|
705 | case TELOPT_TTYPE: |
---|
706 | wont_opt[2] = c; |
---|
707 | net_rawout(wont_opt, sizeof(wont_opt)); |
---|
708 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
709 | cmd(WONT), opt(c)); |
---|
710 | trace_str(trace_msg); |
---|
711 | /* fall through */ |
---|
712 | default: |
---|
713 | myopts[c] = 0; |
---|
714 | break; |
---|
715 | } |
---|
716 | telnet_state = TNS_DATA; |
---|
717 | break; |
---|
718 | case TNS_SB: /* telnet sub-option string command */ |
---|
719 | if (c == IAC) |
---|
720 | telnet_state = TNS_SB_IAC; |
---|
721 | else |
---|
722 | *sbptr++ = c; |
---|
723 | break; |
---|
724 | case TNS_SB_IAC: /* telnet sub-option string command */ |
---|
725 | if (c == SE) { |
---|
726 | telnet_state = TNS_DATA; |
---|
727 | if (sbbuf[0] == TELOPT_TTYPE && sbbuf[1] == TELQUAL_SEND) { |
---|
728 | char *tt_out; |
---|
729 | |
---|
730 | (void) sprintf(trace_msg, "%s %s\n", |
---|
731 | opt(sbbuf[0]), telquals[sbbuf[1]]); |
---|
732 | trace_str(trace_msg); |
---|
733 | |
---|
734 | tt_out = XtMalloc(4 + strlen(termtype) + 2 + 1); |
---|
735 | (void) sprintf(tt_out, "%c%c%c%c%s%c%c", |
---|
736 | IAC, SB, TELOPT_TTYPE, TELQUAL_IS, |
---|
737 | termtype, |
---|
738 | IAC, SE); |
---|
739 | net_rawout((unsigned char *)tt_out, |
---|
740 | 4 + strlen(termtype) + 2); |
---|
741 | XtFree(tt_out); |
---|
742 | |
---|
743 | (void) sprintf(trace_msg, |
---|
744 | "SENT %s %s %s %s %s\n", |
---|
745 | cmd(SB), opt(TELOPT_TTYPE), |
---|
746 | telquals[TELQUAL_IS], termtype, cmd(SE)); |
---|
747 | trace_str(trace_msg); |
---|
748 | check_in3270(); |
---|
749 | } |
---|
750 | } else { |
---|
751 | *sbptr = c; /* just stuff it */ |
---|
752 | telnet_state = TNS_SB; |
---|
753 | } |
---|
754 | break; |
---|
755 | } |
---|
756 | return 0; |
---|
757 | } |
---|
758 | |
---|
759 | |
---|
760 | /* |
---|
761 | * net_exception |
---|
762 | * Called when there is an exceptional condition on the socket. |
---|
763 | */ |
---|
764 | void |
---|
765 | net_exception() |
---|
766 | { |
---|
767 | trace_str("RCVD urgent data indication\n"); |
---|
768 | if (!syncing) { |
---|
769 | syncing = 1; |
---|
770 | x_except_off(); |
---|
771 | } |
---|
772 | } |
---|
773 | |
---|
774 | /* |
---|
775 | * Flavors of Network Output: |
---|
776 | * |
---|
777 | * 3270 mode |
---|
778 | * net_output send a 3270 record |
---|
779 | * |
---|
780 | * ANSI mode; call each other in turn |
---|
781 | * net_sendc net_cookout for 1 byte |
---|
782 | * net_sends net_cookout for a null-terminated string |
---|
783 | * net_cookout send user data with cooked-mode processing, ANSI mode |
---|
784 | * net_cookedout send user data, ANSI mode, already cooked |
---|
785 | * net_rawout send telnet protocol data, ANSI mode |
---|
786 | * |
---|
787 | */ |
---|
788 | |
---|
789 | |
---|
790 | /* |
---|
791 | * net_rawout |
---|
792 | * Send out raw telnet data. We assume that there will always be enough |
---|
793 | * space to buffer what we want to transmit, so we don't handle EAGAIN or |
---|
794 | * EWOULDBLOCK. |
---|
795 | */ |
---|
796 | static void |
---|
797 | net_rawout(buf, len) |
---|
798 | unsigned char *buf; |
---|
799 | int len; |
---|
800 | { |
---|
801 | int nw; |
---|
802 | |
---|
803 | trace_netdata('>', buf, len); |
---|
804 | |
---|
805 | while (len) { |
---|
806 | #if defined(OMTU) /*[*/ |
---|
807 | int n2w = len; |
---|
808 | int pause = 0; |
---|
809 | |
---|
810 | if (n2w > OMTU) { |
---|
811 | n2w = OMTU; |
---|
812 | pause = 1; |
---|
813 | } |
---|
814 | #else |
---|
815 | # define n2w len |
---|
816 | #endif |
---|
817 | nw = write(sock, (char *) buf, n2w); |
---|
818 | if (nw < 0) { |
---|
819 | (void) sprintf(trace_msg, "RCVD socket error %d\n", |
---|
820 | errno); |
---|
821 | trace_str(trace_msg); |
---|
822 | if (errno == EPIPE || errno == ECONNRESET) { |
---|
823 | x_disconnect(False); |
---|
824 | return; |
---|
825 | } else if (errno == EINTR) { |
---|
826 | goto bot; |
---|
827 | } else { |
---|
828 | popup_an_errno(errno, "Socket write"); |
---|
829 | x_disconnect(True); |
---|
830 | return; |
---|
831 | } |
---|
832 | } |
---|
833 | ns_bsent += nw; |
---|
834 | len -= nw; |
---|
835 | buf += nw; |
---|
836 | bot: |
---|
837 | #if defined(OMTU) /*[*/ |
---|
838 | if (pause) |
---|
839 | sleep(1); |
---|
840 | #endif /*]*/ |
---|
841 | ; |
---|
842 | } |
---|
843 | } |
---|
844 | |
---|
845 | |
---|
846 | /* |
---|
847 | * net_cookedout |
---|
848 | * Send user data out in ANSI mode, without cooked-mode processing. |
---|
849 | */ |
---|
850 | static void |
---|
851 | net_cookedout(buf, len) |
---|
852 | char *buf; |
---|
853 | int len; |
---|
854 | { |
---|
855 | int i; |
---|
856 | |
---|
857 | if (toggled(DS_TRACE)) { |
---|
858 | (void) fprintf(tracef, ">"); |
---|
859 | for (i = 0; i < len; i++) |
---|
860 | (void) fprintf(tracef, " %s", ctl_see((int) *(buf+i))); |
---|
861 | (void) fprintf(tracef, "\n"); |
---|
862 | } |
---|
863 | net_rawout((unsigned char *) buf, len); |
---|
864 | } |
---|
865 | |
---|
866 | |
---|
867 | /* |
---|
868 | * net_cookout |
---|
869 | * Send output in ANSI mode, including cooked-mode processing if |
---|
870 | * appropriate. |
---|
871 | */ |
---|
872 | static void |
---|
873 | net_cookout(buf, len) |
---|
874 | char *buf; |
---|
875 | int len; |
---|
876 | { |
---|
877 | |
---|
878 | if (!IN_ANSI || (kybdlock & KL_AWAITING_FIRST)) |
---|
879 | return; |
---|
880 | |
---|
881 | if (linemode) { |
---|
882 | register int i; |
---|
883 | char c; |
---|
884 | |
---|
885 | for (i = 0; i < len; i++) { |
---|
886 | c = buf[i]; |
---|
887 | |
---|
888 | /* Input conversions. */ |
---|
889 | if (!lnext && c == '\r' && appres.icrnl) |
---|
890 | c = '\n'; |
---|
891 | else if (!lnext && c == '\n' && appres.inlcr) |
---|
892 | c = '\r'; |
---|
893 | |
---|
894 | /* Backslashes. */ |
---|
895 | if (c == '\\' && !backslashed) |
---|
896 | backslashed = 1; |
---|
897 | else |
---|
898 | backslashed = 0; |
---|
899 | |
---|
900 | /* Control chars. */ |
---|
901 | if (c == '\n') |
---|
902 | do_eol(c); |
---|
903 | else if (c == vintr) |
---|
904 | do_intr(c); |
---|
905 | else if (c == vquit) |
---|
906 | do_quit(c); |
---|
907 | else if (c == verase) |
---|
908 | do_cerase(c); |
---|
909 | else if (c == vkill) |
---|
910 | do_kill(c); |
---|
911 | else if (c == vwerase) |
---|
912 | do_werase(c); |
---|
913 | else if (c == vrprnt) |
---|
914 | do_rprnt(c); |
---|
915 | else if (c == veof) |
---|
916 | do_eof(c); |
---|
917 | else if (c == vlnext) |
---|
918 | do_lnext(c); |
---|
919 | else |
---|
920 | do_data(c); |
---|
921 | } |
---|
922 | return; |
---|
923 | } else |
---|
924 | net_cookedout(buf, len); |
---|
925 | } |
---|
926 | |
---|
927 | |
---|
928 | /* |
---|
929 | * Cooked mode input processing. |
---|
930 | */ |
---|
931 | |
---|
932 | static void |
---|
933 | cooked_init() |
---|
934 | { |
---|
935 | lbptr = lbuf; |
---|
936 | lnext = 0; |
---|
937 | backslashed = 0; |
---|
938 | } |
---|
939 | |
---|
940 | static void |
---|
941 | ansi_process_s(data) |
---|
942 | char *data; |
---|
943 | { |
---|
944 | while (*data) |
---|
945 | ansi_process((unsigned int) *data++); |
---|
946 | } |
---|
947 | |
---|
948 | static void |
---|
949 | forward_data() |
---|
950 | { |
---|
951 | net_cookedout((char *) lbuf, lbptr - lbuf); |
---|
952 | cooked_init(); |
---|
953 | } |
---|
954 | |
---|
955 | static void |
---|
956 | do_data(c) |
---|
957 | char c; |
---|
958 | { |
---|
959 | if (lbptr+1 < lbuf + sizeof(lbuf)) { |
---|
960 | *lbptr++ = c; |
---|
961 | if (c == '\r') |
---|
962 | *lbptr++ = '\0'; |
---|
963 | if (c == '\t') |
---|
964 | ansi_process((unsigned int) c); |
---|
965 | else |
---|
966 | ansi_process_s(ctl_see((int) c)); |
---|
967 | } else |
---|
968 | ansi_process_s("\007"); |
---|
969 | lnext = 0; |
---|
970 | backslashed = 0; |
---|
971 | } |
---|
972 | |
---|
973 | static void |
---|
974 | do_intr(c) |
---|
975 | char c; |
---|
976 | { |
---|
977 | if (lnext) { |
---|
978 | do_data(c); |
---|
979 | return; |
---|
980 | } |
---|
981 | ansi_process_s(ctl_see((int) c)); |
---|
982 | cooked_init(); |
---|
983 | net_interrupt(); |
---|
984 | } |
---|
985 | |
---|
986 | static void |
---|
987 | do_quit(c) |
---|
988 | char c; |
---|
989 | { |
---|
990 | if (lnext) { |
---|
991 | do_data(c); |
---|
992 | return; |
---|
993 | } |
---|
994 | ansi_process_s(ctl_see((int) c)); |
---|
995 | cooked_init(); |
---|
996 | net_break(); |
---|
997 | } |
---|
998 | |
---|
999 | static void |
---|
1000 | do_cerase(c) |
---|
1001 | char c; |
---|
1002 | { |
---|
1003 | int len; |
---|
1004 | |
---|
1005 | if (backslashed) { |
---|
1006 | lbptr--; |
---|
1007 | ansi_process_s("\b"); |
---|
1008 | do_data(c); |
---|
1009 | return; |
---|
1010 | } |
---|
1011 | if (lnext) { |
---|
1012 | do_data(c); |
---|
1013 | return; |
---|
1014 | } |
---|
1015 | if (lbptr > lbuf) { |
---|
1016 | len = strlen(ctl_see((int) *--lbptr)); |
---|
1017 | |
---|
1018 | while (len--) |
---|
1019 | ansi_process_s("\b \b"); |
---|
1020 | } |
---|
1021 | } |
---|
1022 | |
---|
1023 | static void |
---|
1024 | do_werase(c) |
---|
1025 | char c; |
---|
1026 | { |
---|
1027 | int any = 0; |
---|
1028 | int len; |
---|
1029 | |
---|
1030 | if (lnext) { |
---|
1031 | do_data(c); |
---|
1032 | return; |
---|
1033 | } |
---|
1034 | while (lbptr > lbuf) { |
---|
1035 | char ch = *--lbptr; |
---|
1036 | |
---|
1037 | if (ch == ' ' || ch == '\t') { |
---|
1038 | if (any) { |
---|
1039 | ++lbptr; |
---|
1040 | break; |
---|
1041 | } |
---|
1042 | } else |
---|
1043 | any = 1; |
---|
1044 | len = strlen(ctl_see((int) ch)); |
---|
1045 | |
---|
1046 | while (len--) |
---|
1047 | ansi_process_s("\b \b"); |
---|
1048 | } |
---|
1049 | } |
---|
1050 | |
---|
1051 | static void |
---|
1052 | do_kill(c) |
---|
1053 | char c; |
---|
1054 | { |
---|
1055 | int i, len; |
---|
1056 | |
---|
1057 | if (backslashed) { |
---|
1058 | lbptr--; |
---|
1059 | ansi_process_s("\b"); |
---|
1060 | do_data(c); |
---|
1061 | return; |
---|
1062 | } |
---|
1063 | if (lnext) { |
---|
1064 | do_data(c); |
---|
1065 | return; |
---|
1066 | } |
---|
1067 | while (lbptr > lbuf) { |
---|
1068 | len = strlen(ctl_see((int) *--lbptr)); |
---|
1069 | |
---|
1070 | for (i = 0; i < len; i++) |
---|
1071 | ansi_process_s("\b \b"); |
---|
1072 | } |
---|
1073 | } |
---|
1074 | |
---|
1075 | static void |
---|
1076 | do_rprnt(c) |
---|
1077 | char c; |
---|
1078 | { |
---|
1079 | unsigned char *p; |
---|
1080 | |
---|
1081 | if (lnext) { |
---|
1082 | do_data(c); |
---|
1083 | return; |
---|
1084 | } |
---|
1085 | ansi_process_s(ctl_see((int) c)); |
---|
1086 | ansi_process_s("\r\n"); |
---|
1087 | for (p = lbuf; p < lbptr; p++) |
---|
1088 | ansi_process_s(ctl_see((int) *p)); |
---|
1089 | } |
---|
1090 | |
---|
1091 | static void |
---|
1092 | do_eof(c) |
---|
1093 | char c; |
---|
1094 | { |
---|
1095 | if (backslashed) { |
---|
1096 | lbptr--; |
---|
1097 | ansi_process_s("\b"); |
---|
1098 | do_data(c); |
---|
1099 | return; |
---|
1100 | } |
---|
1101 | if (lnext) { |
---|
1102 | do_data(c); |
---|
1103 | return; |
---|
1104 | } |
---|
1105 | do_data(c); |
---|
1106 | forward_data(); |
---|
1107 | } |
---|
1108 | |
---|
1109 | static void |
---|
1110 | do_eol(c) |
---|
1111 | char c; |
---|
1112 | { |
---|
1113 | if (lnext) { |
---|
1114 | do_data(c); |
---|
1115 | return; |
---|
1116 | } |
---|
1117 | if (lbptr+2 >= lbuf + sizeof(lbuf)) { |
---|
1118 | ansi_process_s("\007"); |
---|
1119 | return; |
---|
1120 | } |
---|
1121 | *lbptr++ = '\r'; |
---|
1122 | *lbptr++ = '\n'; |
---|
1123 | ansi_process_s("\r\n"); |
---|
1124 | forward_data(); |
---|
1125 | } |
---|
1126 | |
---|
1127 | static void |
---|
1128 | do_lnext(c) |
---|
1129 | char c; |
---|
1130 | { |
---|
1131 | if (lnext) { |
---|
1132 | do_data(c); |
---|
1133 | return; |
---|
1134 | } |
---|
1135 | lnext = 1; |
---|
1136 | ansi_process_s("^\b"); |
---|
1137 | } |
---|
1138 | |
---|
1139 | |
---|
1140 | |
---|
1141 | /* |
---|
1142 | * check_in3270 |
---|
1143 | * Check for mode switches between ANSI and 3270 mode. |
---|
1144 | */ |
---|
1145 | static void |
---|
1146 | check_in3270() |
---|
1147 | { |
---|
1148 | Boolean now3270 = myopts[TELOPT_BINARY] && |
---|
1149 | myopts[TELOPT_EOR] && |
---|
1150 | myopts[TELOPT_TTYPE] && |
---|
1151 | hisopts[TELOPT_BINARY] && |
---|
1152 | hisopts[TELOPT_EOR]; |
---|
1153 | |
---|
1154 | if (now3270 != IN_3270) { |
---|
1155 | (void) sprintf(trace_msg, "No%c operating in 3270 mode.\n", |
---|
1156 | now3270 ? 'w' : 't'); |
---|
1157 | trace_str(trace_msg); |
---|
1158 | x_in3270(now3270); |
---|
1159 | |
---|
1160 | /* Allocate the initial 3270 input buffer. */ |
---|
1161 | if (now3270 && !ibuf_size) { |
---|
1162 | ibuf = (unsigned char *)XtMalloc(BUFSIZ); |
---|
1163 | ibuf_size = BUFSIZ; |
---|
1164 | ibptr = ibuf; |
---|
1165 | } |
---|
1166 | } |
---|
1167 | } |
---|
1168 | |
---|
1169 | /* |
---|
1170 | * store3270in |
---|
1171 | * Store a character in the 3270 input buffer, checking for buffer |
---|
1172 | * overflow and reallocating ibuf if necessary. |
---|
1173 | */ |
---|
1174 | static void |
---|
1175 | store3270in(c) |
---|
1176 | unsigned char c; |
---|
1177 | { |
---|
1178 | if (ibptr - ibuf >= ibuf_size) { |
---|
1179 | ibuf_size += BUFSIZ; |
---|
1180 | ibuf = (unsigned char *)XtRealloc((char *)ibuf, ibuf_size); |
---|
1181 | ibptr = ibuf + ibuf_size - BUFSIZ; |
---|
1182 | } |
---|
1183 | *ibptr++ = c; |
---|
1184 | } |
---|
1185 | |
---|
1186 | /* |
---|
1187 | * space3270out |
---|
1188 | * Ensure that <n> more characters will fit in the 3270 output buffer. |
---|
1189 | */ |
---|
1190 | void |
---|
1191 | space3270out(n) |
---|
1192 | int n; |
---|
1193 | { |
---|
1194 | if (obuf_size == 0) { |
---|
1195 | obuf_size = BUFSIZ; |
---|
1196 | obuf = (unsigned char *)XtMalloc(obuf_size); |
---|
1197 | obptr = obuf; |
---|
1198 | return; |
---|
1199 | } |
---|
1200 | if ((obptr + n) - obuf >= obuf_size) { |
---|
1201 | int nc = obptr - obuf; |
---|
1202 | |
---|
1203 | obuf_size += BUFSIZ; |
---|
1204 | obuf = (unsigned char *)XtRealloc((char *)obuf, obuf_size); |
---|
1205 | obptr = obuf + nc; |
---|
1206 | } |
---|
1207 | } |
---|
1208 | |
---|
1209 | |
---|
1210 | /* |
---|
1211 | * check_linemode |
---|
1212 | * Set the global variable 'linemode', which says whether we are in |
---|
1213 | * character-by-character mode or line mode. |
---|
1214 | */ |
---|
1215 | static void |
---|
1216 | check_linemode(init) |
---|
1217 | Boolean init; |
---|
1218 | { |
---|
1219 | int wasline = linemode; |
---|
1220 | |
---|
1221 | /* |
---|
1222 | * The next line is a deliberate kluge to effectively ignore the SGA |
---|
1223 | * option. If the host will echo for us, we assume |
---|
1224 | * character-at-a-time; otherwise we assume fully cooked by us. |
---|
1225 | * |
---|
1226 | * This allows certain IBM hosts which volunteer SGA but refuse |
---|
1227 | * ECHO to operate more-or-less normally, at the expense of |
---|
1228 | * implementing the (hopefully useless) "character-at-a-time, local |
---|
1229 | * echo" mode. |
---|
1230 | * |
---|
1231 | * We still implement "switch to line mode" and "switch to character |
---|
1232 | * mode" properly by asking for both SGA and ECHO to be off or on, but |
---|
1233 | * we basically ignore the reply for SGA. |
---|
1234 | */ |
---|
1235 | linemode = !hisopts[TELOPT_ECHO] /* && !hisopts[TELOPT_SGA] */; |
---|
1236 | |
---|
1237 | if (init || linemode != wasline) { |
---|
1238 | menubar_newmode(); |
---|
1239 | if (!init) { |
---|
1240 | (void) sprintf(trace_msg, "Operating in %s mode.\n", |
---|
1241 | linemode ? "line" : "character-at-a-time"); |
---|
1242 | trace_str(trace_msg); |
---|
1243 | } |
---|
1244 | if (linemode) |
---|
1245 | cooked_init(); |
---|
1246 | } |
---|
1247 | } |
---|
1248 | |
---|
1249 | |
---|
1250 | /* |
---|
1251 | * nnn |
---|
1252 | * Expands a number to a character string, for displaying unknown telnet |
---|
1253 | * commands and options. |
---|
1254 | */ |
---|
1255 | static char * |
---|
1256 | nnn(c) |
---|
1257 | int c; |
---|
1258 | { |
---|
1259 | static char buf[64]; |
---|
1260 | |
---|
1261 | (void) sprintf(buf, "%d", c); |
---|
1262 | return buf; |
---|
1263 | } |
---|
1264 | |
---|
1265 | #if !defined(TELCMD) || !defined(TELCMD_OK) /*[*/ |
---|
1266 | #undef TELCMD |
---|
1267 | #define TELCMD(x) telcmds[(x) - SE] |
---|
1268 | #undef TELCMD_OK |
---|
1269 | #define TELCMD_OK(x) ((x) >= SE && (x) <= IAC) |
---|
1270 | #endif /*]*/ |
---|
1271 | |
---|
1272 | /* |
---|
1273 | * cmd |
---|
1274 | * Expands a TELNET command into a character string. |
---|
1275 | */ |
---|
1276 | static char * |
---|
1277 | cmd(c) |
---|
1278 | unsigned char c; |
---|
1279 | { |
---|
1280 | if (TELCMD_OK(c)) |
---|
1281 | return TELCMD(c); |
---|
1282 | else |
---|
1283 | return nnn((int)c); |
---|
1284 | } |
---|
1285 | |
---|
1286 | #if !defined(TELOPT) || !defined(TELOPT_OK) /*[*/ |
---|
1287 | #undef TELOPT |
---|
1288 | #define TELOPT(x) telopts[(x)] |
---|
1289 | #undef TELOPT_OK |
---|
1290 | #define TELOPT_OK(x) ((x) >= TELOPT_BINARY && (x) <= TELOPT_EOR) |
---|
1291 | #endif /*]*/ |
---|
1292 | |
---|
1293 | /* |
---|
1294 | * opt |
---|
1295 | * Expands a TELNET option into a character string. |
---|
1296 | */ |
---|
1297 | static char * |
---|
1298 | opt(c) |
---|
1299 | unsigned char c; |
---|
1300 | { |
---|
1301 | if (TELOPT_OK(c)) |
---|
1302 | return TELOPT(c); |
---|
1303 | else if (c == TELOPT_TN3270E) |
---|
1304 | return "TN3270E"; |
---|
1305 | else |
---|
1306 | return nnn((int)c); |
---|
1307 | } |
---|
1308 | |
---|
1309 | |
---|
1310 | #define LINEDUMP_MAX 32 |
---|
1311 | |
---|
1312 | void |
---|
1313 | trace_netdata(direction, buf, len) |
---|
1314 | char direction; |
---|
1315 | unsigned char *buf; |
---|
1316 | int len; |
---|
1317 | { |
---|
1318 | int offset; |
---|
1319 | struct timeval ts; |
---|
1320 | double tdiff; |
---|
1321 | |
---|
1322 | if (!toggled(DS_TRACE)) |
---|
1323 | return; |
---|
1324 | (void) gettimeofday(&ts, (struct timezone *)NULL); |
---|
1325 | if (IN_3270) { |
---|
1326 | tdiff = ((1.0e6 * (double)(ts.tv_sec - ds_ts.tv_sec)) + |
---|
1327 | (double)(ts.tv_usec - ds_ts.tv_usec)) / 1.0e6; |
---|
1328 | (void) fprintf(tracef, "%c +%gs\n", direction, tdiff); |
---|
1329 | } |
---|
1330 | ds_ts = ts; |
---|
1331 | for (offset = 0; offset < len; offset++) { |
---|
1332 | if (!(offset % LINEDUMP_MAX)) |
---|
1333 | (void) fprintf(tracef, "%s%c 0x%-3x ", |
---|
1334 | (offset ? "\n" : ""), direction, offset); |
---|
1335 | (void) fprintf(tracef, "%02x", buf[offset]); |
---|
1336 | } |
---|
1337 | (void) fprintf(tracef, "\n"); |
---|
1338 | } |
---|
1339 | |
---|
1340 | |
---|
1341 | /* |
---|
1342 | * net_output |
---|
1343 | * Send 3270 output over the network, tacking on the necessary |
---|
1344 | * telnet end-of-record command. |
---|
1345 | */ |
---|
1346 | void |
---|
1347 | net_output() |
---|
1348 | { |
---|
1349 | /* Count the number of IACs in the message. */ |
---|
1350 | { |
---|
1351 | char *buf = (char *)obuf; |
---|
1352 | int len = obptr - obuf; |
---|
1353 | char *iac; |
---|
1354 | int cnt = 0; |
---|
1355 | |
---|
1356 | while (len && (iac = memchr(buf, IAC, len)) != CN) { |
---|
1357 | cnt++; |
---|
1358 | len -= iac - buf + 1; |
---|
1359 | buf = iac + 1; |
---|
1360 | } |
---|
1361 | if (cnt) { |
---|
1362 | space3270out(cnt); |
---|
1363 | len = obptr - obuf; |
---|
1364 | buf = (char *)obuf; |
---|
1365 | |
---|
1366 | /* Now quote them. */ |
---|
1367 | while (len && (iac = memchr(buf, IAC, len)) != CN) { |
---|
1368 | int ml = len - (iac - buf); |
---|
1369 | |
---|
1370 | MEMORY_MOVE(iac + 1, iac, ml); |
---|
1371 | len -= iac - buf + 1; |
---|
1372 | buf = iac + 2; |
---|
1373 | obptr++; |
---|
1374 | } |
---|
1375 | } |
---|
1376 | } |
---|
1377 | |
---|
1378 | /* Add IAC EOR to the end and send it. */ |
---|
1379 | space3270out(2); |
---|
1380 | *obptr++ = IAC; |
---|
1381 | *obptr++ = EOR; |
---|
1382 | net_rawout(obuf, obptr - obuf); |
---|
1383 | |
---|
1384 | trace_str("SENT EOR\n"); |
---|
1385 | ns_rsent++; |
---|
1386 | cooked_init(); /* in case we go back to ANSI mode */ |
---|
1387 | } |
---|
1388 | |
---|
1389 | /* |
---|
1390 | * Add IAC EOR to a buffer. |
---|
1391 | */ |
---|
1392 | void |
---|
1393 | net_add_eor(buf, len) |
---|
1394 | unsigned char *buf; |
---|
1395 | int len; |
---|
1396 | { |
---|
1397 | buf[len++] = IAC; |
---|
1398 | buf[len++] = EOR; |
---|
1399 | } |
---|
1400 | |
---|
1401 | |
---|
1402 | /* |
---|
1403 | * net_sendc |
---|
1404 | * Send a character of user data over the network in ANSI mode. |
---|
1405 | */ |
---|
1406 | void |
---|
1407 | net_sendc(c) |
---|
1408 | char c; |
---|
1409 | { |
---|
1410 | if (c == '\r' && !linemode) /* CR must be quoted */ |
---|
1411 | net_cookout("\r\n", 2); |
---|
1412 | else |
---|
1413 | net_cookout(&c, 1); |
---|
1414 | } |
---|
1415 | |
---|
1416 | |
---|
1417 | /* |
---|
1418 | * net_sends |
---|
1419 | * Send a null-terminated string of user data in ANSI mode. |
---|
1420 | */ |
---|
1421 | void |
---|
1422 | net_sends(s) |
---|
1423 | char *s; |
---|
1424 | { |
---|
1425 | net_cookout(s, strlen(s)); |
---|
1426 | } |
---|
1427 | |
---|
1428 | |
---|
1429 | /* |
---|
1430 | * net_send_erase |
---|
1431 | * Sends the KILL character in ANSI mode. |
---|
1432 | */ |
---|
1433 | void |
---|
1434 | net_send_erase() |
---|
1435 | { |
---|
1436 | net_cookout(&verase, 1); |
---|
1437 | } |
---|
1438 | |
---|
1439 | |
---|
1440 | /* |
---|
1441 | * net_send_kill |
---|
1442 | * Sends the KILL character in ANSI mode. |
---|
1443 | */ |
---|
1444 | void |
---|
1445 | net_send_kill() |
---|
1446 | { |
---|
1447 | net_cookout(&vkill, 1); |
---|
1448 | } |
---|
1449 | |
---|
1450 | |
---|
1451 | /* |
---|
1452 | * net_send_werase |
---|
1453 | * Sends the WERASE character in ANSI mode. |
---|
1454 | */ |
---|
1455 | void |
---|
1456 | net_send_werase() |
---|
1457 | { |
---|
1458 | net_cookout(&vwerase, 1); |
---|
1459 | } |
---|
1460 | |
---|
1461 | |
---|
1462 | /* |
---|
1463 | * External entry points to negotiate line or character mode. |
---|
1464 | */ |
---|
1465 | void |
---|
1466 | net_linemode() |
---|
1467 | { |
---|
1468 | if (!CONNECTED) |
---|
1469 | return; |
---|
1470 | if (hisopts[TELOPT_ECHO]) { |
---|
1471 | dont_opt[2] = TELOPT_ECHO; |
---|
1472 | net_rawout(dont_opt, sizeof(dont_opt)); |
---|
1473 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
1474 | cmd(DONT), opt(TELOPT_ECHO)); |
---|
1475 | trace_str(trace_msg); |
---|
1476 | } |
---|
1477 | if (hisopts[TELOPT_SGA]) { |
---|
1478 | dont_opt[2] = TELOPT_SGA; |
---|
1479 | net_rawout(dont_opt, sizeof(dont_opt)); |
---|
1480 | (void) sprintf(trace_msg, "SENT %s %s\n", |
---|
1481 | cmd(DONT), opt(TELOPT_SGA)); |
---|
1482 | trace_str(trace_msg); |
---|
1483 | } |
---|
1484 | } |
---|
1485 | |
---|
1486 | void |
---|
1487 | net_charmode() |
---|
1488 | { |
---|
1489 | if (!CONNECTED) |
---|
1490 | return; |
---|
1491 | if (!hisopts[TELOPT_ECHO]) { |
---|
1492 | do_opt[2] = TELOPT_ECHO; |
---|
1493 | net_rawout(do_opt, sizeof(do_opt)); |
---|
1494 | (void) sprintf(trace_msg, "SENT %s %s\n", cmd(DO), |
---|
1495 | opt(TELOPT_ECHO)); |
---|
1496 | trace_str(trace_msg); |
---|
1497 | } |
---|
1498 | if (!hisopts[TELOPT_SGA]) { |
---|
1499 | do_opt[2] = TELOPT_SGA; |
---|
1500 | net_rawout(do_opt, sizeof(do_opt)); |
---|
1501 | (void) sprintf(trace_msg, "SENT %s %s\n", cmd(DO), |
---|
1502 | opt(TELOPT_SGA)); |
---|
1503 | trace_str(trace_msg); |
---|
1504 | } |
---|
1505 | } |
---|
1506 | |
---|
1507 | |
---|
1508 | /* |
---|
1509 | * net_break |
---|
1510 | * Send telnet break, which is used to implement 3270 ATTN. |
---|
1511 | * |
---|
1512 | */ |
---|
1513 | void |
---|
1514 | net_break() |
---|
1515 | { |
---|
1516 | static unsigned char buf[] = { IAC, BREAK }; |
---|
1517 | |
---|
1518 | /* I don't know if we should first send TELNET synch ? */ |
---|
1519 | net_rawout(buf, sizeof(buf)); |
---|
1520 | trace_str("SENT BREAK\n"); |
---|
1521 | } |
---|
1522 | |
---|
1523 | /* |
---|
1524 | * net_interrupt |
---|
1525 | * Send telnet IP. |
---|
1526 | * |
---|
1527 | */ |
---|
1528 | static void |
---|
1529 | net_interrupt() |
---|
1530 | { |
---|
1531 | static unsigned char buf[] = { IAC, IP }; |
---|
1532 | |
---|
1533 | /* I don't know if we should first send TELNET synch ? */ |
---|
1534 | net_rawout(buf, sizeof(buf)); |
---|
1535 | trace_str("SENT IP\n"); |
---|
1536 | } |
---|
1537 | |
---|
1538 | /* |
---|
1539 | * parse_ctlchar |
---|
1540 | * Parse an stty control-character specification. |
---|
1541 | * A cheap, non-complaining implementation. |
---|
1542 | */ |
---|
1543 | static char |
---|
1544 | parse_ctlchar(s) |
---|
1545 | char *s; |
---|
1546 | { |
---|
1547 | if (!s || !*s) |
---|
1548 | return 0; |
---|
1549 | if ((int) strlen(s) > 1) { |
---|
1550 | if (*s != '^') |
---|
1551 | return 0; |
---|
1552 | else if (*(s+1) == '?') |
---|
1553 | return 0177; |
---|
1554 | else |
---|
1555 | return *(s+1) - '@'; |
---|
1556 | } else |
---|
1557 | return *s; |
---|
1558 | } |
---|
1559 | |
---|
1560 | /* |
---|
1561 | * net_linemode_chars |
---|
1562 | * Report line-mode characters. |
---|
1563 | */ |
---|
1564 | struct ctl_char * |
---|
1565 | net_linemode_chars() |
---|
1566 | { |
---|
1567 | static struct ctl_char c[9]; |
---|
1568 | |
---|
1569 | c[0].name = "intr"; (void) strcpy(c[0].value, ctl_see(vintr)); |
---|
1570 | c[1].name = "quit"; (void) strcpy(c[1].value, ctl_see(vquit)); |
---|
1571 | c[2].name = "erase"; (void) strcpy(c[2].value, ctl_see(verase)); |
---|
1572 | c[3].name = "kill"; (void) strcpy(c[3].value, ctl_see(vkill)); |
---|
1573 | c[4].name = "eof"; (void) strcpy(c[4].value, ctl_see(veof)); |
---|
1574 | c[5].name = "werase"; (void) strcpy(c[5].value, ctl_see(vwerase)); |
---|
1575 | c[6].name = "rprnt"; (void) strcpy(c[6].value, ctl_see(vrprnt)); |
---|
1576 | c[7].name = "lnext"; (void) strcpy(c[7].value, ctl_see(vlnext)); |
---|
1577 | c[8].name = 0; |
---|
1578 | |
---|
1579 | return c; |
---|
1580 | } |
---|
1581 | |
---|
1582 | /* |
---|
1583 | * Construct a string to reproduce the current TELNET options. |
---|
1584 | * Returns a Boolean indicating whether it is necessary. |
---|
1585 | */ |
---|
1586 | Boolean |
---|
1587 | net_snap_options() |
---|
1588 | { |
---|
1589 | Boolean any = False; |
---|
1590 | int i; |
---|
1591 | static unsigned char ttype_str[] = { |
---|
1592 | IAC, DO, TELOPT_TTYPE, |
---|
1593 | IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE |
---|
1594 | }; |
---|
1595 | |
---|
1596 | if (!CONNECTED) |
---|
1597 | return False; |
---|
1598 | |
---|
1599 | obptr = obuf; |
---|
1600 | |
---|
1601 | /* Do TTYPE first. */ |
---|
1602 | if (myopts[TELOPT_TTYPE]) { |
---|
1603 | space3270out(sizeof(ttype_str)); |
---|
1604 | for (i = 0; i < sizeof(ttype_str); i++) |
---|
1605 | *obptr++ = ttype_str[i]; |
---|
1606 | } |
---|
1607 | |
---|
1608 | /* Do the other options. */ |
---|
1609 | for (i = 0; i < N_OPTS; i++) { |
---|
1610 | space3270out(6); |
---|
1611 | if (i == TELOPT_TTYPE) |
---|
1612 | continue; |
---|
1613 | if (hisopts[i]) { |
---|
1614 | *obptr++ = IAC; |
---|
1615 | *obptr++ = WILL; |
---|
1616 | *obptr++ = (unsigned char)i; |
---|
1617 | any = True; |
---|
1618 | } |
---|
1619 | if (myopts[i]) { |
---|
1620 | *obptr++ = IAC; |
---|
1621 | *obptr++ = DO; |
---|
1622 | *obptr++ = (unsigned char)i; |
---|
1623 | any = True; |
---|
1624 | } |
---|
1625 | } |
---|
1626 | return any; |
---|
1627 | } |
---|
1628 | |
---|
1629 | /* |
---|
1630 | * Set blocking/non-blocking mode on the socket. On error, pops up an error |
---|
1631 | * message, but does not close the socket. |
---|
1632 | */ |
---|
1633 | static int |
---|
1634 | non_blocking(on) |
---|
1635 | Boolean on; |
---|
1636 | { |
---|
1637 | #if !defined(BLOCKING_CONNECT_ONLY) /*[*/ |
---|
1638 | # if defined(FIONBIO) /*[*/ |
---|
1639 | int i = on ? 1 : 0; |
---|
1640 | |
---|
1641 | if (ioctl(sock, FIONBIO, &i) < 0) { |
---|
1642 | popup_an_errno(errno, "ioctl(FIONBIO)"); |
---|
1643 | return -1; |
---|
1644 | } |
---|
1645 | # else /*][*/ |
---|
1646 | int f; |
---|
1647 | |
---|
1648 | if ((f = fcntl(sock, F_GETFL, 0)) == -1) { |
---|
1649 | popup_an_errno(errno, "fcntl(F_GETFL)"); |
---|
1650 | return -1; |
---|
1651 | } |
---|
1652 | if (on) |
---|
1653 | f |= O_NDELAY; |
---|
1654 | else |
---|
1655 | f &= ~O_NDELAY; |
---|
1656 | if (fcntl(sock, F_SETFL, f) < 0) { |
---|
1657 | popup_an_errno(errno, "fcntl(F_SETFL)"); |
---|
1658 | return -1; |
---|
1659 | } |
---|
1660 | # endif /*]*/ |
---|
1661 | #endif /*]*/ |
---|
1662 | return 0; |
---|
1663 | } |
---|
1664 | |
---|
1665 | static void |
---|
1666 | trace_str(s) |
---|
1667 | char *s; |
---|
1668 | { |
---|
1669 | if (!toggled(DS_TRACE)) |
---|
1670 | return; |
---|
1671 | (void) fprintf(tracef, "%s", s); |
---|
1672 | } |
---|