1 /*
2 * Copyright (c) 1994-2009, 2013-2014 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of Paul Mattes nor the names of his contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * sf.c
30 * This module handles 3270 structured fields.
31 *
32 */
33
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include "globals.h"
38 #include "3270ds.h"
39 #include <string.h>
40
41 #include "ctlrc.h"
42 #if !defined(PR3287) /*[*/
43 # include "ft_dft.h"
44 #endif /*]*/
45 #include "sf.h"
46 #if defined(_WIN32) /*[*/
47 # include "ws2tcpip.h"
48 #else /*][*/
49 # include <sys/socket.h>
50 #endif /*]*/
51 #include "telnet_core.h"
52 #include "trace.h"
53
54 /* Statics */
55 static bool qr_in_progress = false;
56 static enum pds sf_read_part(unsigned char buf[], unsigned buflen);
57 static enum pds sf_erase_reset(unsigned char buf[], int buflen);
58 static enum pds sf_set_reply_mode(unsigned char buf[], int buflen);
59 static enum pds sf_outbound_ds(unsigned char buf[], int buflen);
60 static void query_reply_start(void);
61 static void do_query_reply(unsigned char code);
62 static void query_reply_end(void);
63
64 /* Some permanent substitutions. */
65 #define maxROWS 72
66 #define maxCOLS 66
67 #define char_width 10
68 #define char_height 20
69 #define standard_font 0
70
71 static unsigned char supported_replies[] = {
72 QR_SUMMARY, /* 0x80 */
73 QR_USABLE_AREA, /* 0x81 */
74 QR_ALPHA_PART, /* 0x84 */
75 QR_CHARSETS, /* 0x85 */
76 QR_COLOR, /* 0x86 */
77 QR_HIGHLIGHTING, /* 0x87 */
78 QR_REPLY_MODES, /* 0x88 */
79 QR_DBCS_ASIA, /* 0x91 */
80 QR_IMP_PART, /* 0xa6 */
81 QR_DDM, /* 0x95 */
82 };
83 #define NSR (sizeof(supported_replies)/sizeof(unsigned char))
84
85
86 /*
87 * Process a 3270 Write Structured Field command
88 */
89 enum pds
write_structured_field(unsigned char buf[],size_t buflen)90 write_structured_field(unsigned char buf[], size_t buflen)
91 {
92 size_t fieldlen;
93 unsigned char *cp = buf;
94 bool first = true;
95 enum pds rv = PDS_OKAY_NO_OUTPUT;
96 enum pds rv_this = PDS_OKAY_NO_OUTPUT;
97 bool bad_cmd = false;
98
99 /* Skip the WSF command itself. */
100 cp++;
101 buflen--;
102
103 /* Interpret fields. */
104 while (buflen > 0) {
105
106 if (first) {
107 trace_ds(" ");
108 } else {
109 trace_ds("< WriteStructuredField ");
110 }
111 first = false;
112
113 /* Pick out the field length. */
114 if (buflen < 2) {
115 trace_ds("error: single byte at end of message\n");
116 return rv ? rv : PDS_BAD_CMD;
117 }
118 fieldlen = (cp[0] << 8) + cp[1];
119 if (fieldlen == 0) {
120 fieldlen = buflen;
121 }
122 if (fieldlen < 3) {
123 trace_ds("error: field length %d too small\n", (int)fieldlen);
124 return rv ? rv : PDS_BAD_CMD;
125 }
126 if (fieldlen > buflen) {
127 trace_ds("error: field length %d exceeds remaining "
128 "message length %d\n",
129 (int)fieldlen, (int)buflen);
130 return rv ? rv : PDS_BAD_CMD;
131 }
132
133 /* Dispatch on the ID. */
134 switch (cp[2]) {
135 case SF_READ_PART:
136 trace_ds("ReadPartition");
137 rv_this = sf_read_part(cp, (int)fieldlen);
138 break;
139 case SF_ERASE_RESET:
140 trace_ds("EraseReset");
141 rv_this = sf_erase_reset(cp, (int)fieldlen);
142 break;
143 case SF_SET_REPLY_MODE:
144 trace_ds("SetReplyMode");
145 rv_this = sf_set_reply_mode(cp, (int)fieldlen);
146 break;
147 case SF_OUTBOUND_DS:
148 trace_ds("OutboundDS");
149 rv_this = sf_outbound_ds(cp, (int)fieldlen);
150 break;
151 #if !defined(PR3287) /*[*/
152 case SF_TRANSFER_DATA: /* File transfer data */
153 trace_ds("FileTransferData");
154 ft_dft_data(cp, (int)fieldlen);
155 break;
156 #endif /*]*/
157 default:
158 trace_ds("unsupported ID 0x%02x\n", cp[2]);
159 rv_this = PDS_BAD_CMD;
160 break;
161 }
162
163 /*
164 * Accumulate errors or output flags.
165 * One real ugliness here is that if we have already
166 * generated some output, then we have already positively
167 * acknowledged the request, so if we fail here, we have no
168 * way to return the error indication.
169 */
170 if (rv_this < 0) {
171 bad_cmd = true;
172 } else {
173 rv |= rv_this;
174 }
175
176 /* Skip to the next field. */
177 cp += fieldlen;
178 buflen -= fieldlen;
179 }
180 if (first) {
181 trace_ds(" (null)\n");
182 }
183
184 if (bad_cmd && !rv) {
185 return PDS_BAD_CMD;
186 } else {
187 return rv;
188 }
189 }
190
191 static enum pds
sf_read_part(unsigned char buf[],unsigned buflen)192 sf_read_part(unsigned char buf[], unsigned buflen)
193 {
194 unsigned char partition;
195 unsigned i;
196 int any = 0;
197 const char *comma = "";
198
199 if (buflen < 5) {
200 trace_ds(" error: field length %d too small\n", buflen);
201 return PDS_BAD_CMD;
202 }
203
204 partition = buf[3];
205 trace_ds("(0x%02x)", partition);
206
207 switch (buf[4]) {
208 case SF_RP_QUERY:
209 trace_ds(" Query");
210 if (partition != 0xff) {
211 trace_ds(" error: illegal partition\n");
212 return PDS_BAD_CMD;
213 }
214 trace_ds("\n");
215 query_reply_start();
216 for (i = 0; i < NSR; i++) {
217 if (dbcs || supported_replies[i] != QR_DBCS_ASIA) {
218 do_query_reply(supported_replies[i]);
219 }
220 }
221 query_reply_end();
222 break;
223 case SF_RP_QLIST:
224 trace_ds(" QueryList ");
225 if (partition != 0xff) {
226 trace_ds("error: illegal partition\n");
227 return PDS_BAD_CMD;
228 }
229 if (buflen < 6) {
230 trace_ds("error: missing request type\n");
231 return PDS_BAD_CMD;
232 }
233 query_reply_start();
234 switch (buf[5]) {
235 case SF_RPQ_LIST:
236 trace_ds("List(");
237 if (buflen < 7) {
238 trace_ds(")\n");
239 do_query_reply(QR_NULL);
240 } else {
241 for (i = 6; i < buflen; i++) {
242 trace_ds("%s%s", comma, see_qcode(buf[i]));
243 comma = ",";
244 }
245 trace_ds(")\n");
246 for (i = 0; i < NSR; i++) {
247 if (memchr((char *)&buf[6], (char)supported_replies[i],
248 buflen - 6) &&
249 (dbcs || supported_replies[i] != QR_DBCS_ASIA)) {
250 do_query_reply(supported_replies[i]);
251 any++;
252 }
253 }
254 if (!any) {
255 do_query_reply(QR_NULL);
256 }
257 }
258 break;
259 case SF_RPQ_EQUIV:
260 trace_ds("Equivlent+List(");
261 for (i = 6; i < buflen; i++) {
262 trace_ds("%s%s", comma, see_qcode(buf[i]));
263 comma = ",";
264 }
265 trace_ds(")\n");
266 for (i = 0; i < NSR; i++) {
267 if (dbcs || supported_replies[i] != QR_DBCS_ASIA) {
268 do_query_reply(supported_replies[i]);
269 }
270 }
271 break;
272 case SF_RPQ_ALL:
273 trace_ds("All\n");
274 for (i = 0; i < NSR; i++) {
275 if (dbcs || supported_replies[i] != QR_DBCS_ASIA) {
276 do_query_reply(supported_replies[i]);
277 }
278 }
279 break;
280 default:
281 trace_ds("unknown request type 0x%02x\n", buf[5]);
282 return PDS_BAD_CMD;
283 }
284 query_reply_end();
285 break;
286 case SNA_CMD_RMA:
287 trace_ds(" ReadModifiedAll");
288 if (partition != 0x00) {
289 trace_ds(" error: illegal partition\n");
290 return PDS_BAD_CMD;
291 }
292 trace_ds("\n");
293 return PDS_BAD_CMD;
294 break;
295 case SNA_CMD_RB:
296 trace_ds(" ReadBuffer");
297 if (partition != 0x00) {
298 trace_ds(" error: illegal partition\n");
299 return PDS_BAD_CMD;
300 }
301 trace_ds("\n");
302 return PDS_BAD_CMD;
303 break;
304 case SNA_CMD_RM:
305 trace_ds(" ReadModified");
306 if (partition != 0x00) {
307 trace_ds(" error: illegal partition\n");
308 return PDS_BAD_CMD;
309 }
310 trace_ds("\n");
311 return PDS_BAD_CMD;
312 break;
313 default:
314 trace_ds(" unknown type 0x%02x\n", buf[4]);
315 return PDS_BAD_CMD;
316 }
317
318 return PDS_OKAY_OUTPUT;
319 }
320
321 static enum pds
sf_erase_reset(unsigned char buf[],int buflen)322 sf_erase_reset(unsigned char buf[], int buflen)
323 {
324 if (buflen != 4) {
325 trace_ds(" error: wrong field length %d\n", buflen);
326 return PDS_BAD_CMD;
327 }
328
329 switch (buf[3]) {
330 case SF_ER_DEFAULT:
331 trace_ds(" Default\n");
332 break;
333 case SF_ER_ALT:
334 trace_ds(" Alternate\n");
335 break;
336 default:
337 trace_ds(" unknown type 0x%02x\n", buf[3]);
338 return PDS_BAD_CMD;
339 }
340
341 return PDS_OKAY_NO_OUTPUT;
342 }
343
344 static enum pds
sf_set_reply_mode(unsigned char buf[],int buflen)345 sf_set_reply_mode(unsigned char buf[], int buflen)
346 {
347 unsigned char partition;
348
349 if (buflen < 5) {
350 trace_ds(" error: wrong field length %d\n", buflen);
351 return PDS_BAD_CMD;
352 }
353
354 partition = buf[3];
355 trace_ds("(0x%02x)", partition);
356 if (partition != 0x00) {
357 trace_ds(" error: illegal partition\n");
358 return PDS_BAD_CMD;
359 }
360
361 switch (buf[4]) {
362 case SF_SRM_FIELD:
363 trace_ds(" Field\n");
364 break;
365 case SF_SRM_XFIELD:
366 trace_ds(" ExtendedField\n");
367 break;
368 case SF_SRM_CHAR:
369 trace_ds(" Character");
370 break;
371 default:
372 trace_ds(" unknown mode 0x%02x\n", buf[4]);
373 return PDS_BAD_CMD;
374 }
375
376 return PDS_OKAY_NO_OUTPUT;
377 }
378
379 static enum pds
sf_outbound_ds(unsigned char buf[],int buflen)380 sf_outbound_ds(unsigned char buf[], int buflen)
381 {
382 if (buflen < 5) {
383 trace_ds(" error: field length %d too short\n", buflen);
384 return PDS_BAD_CMD;
385 }
386
387 trace_ds("(0x%02x)", buf[3]);
388 if (buf[3] != 0x00) {
389 trace_ds(" error: illegal partition 0x%0x\n", buf[3]);
390 return PDS_BAD_CMD;
391 }
392
393 switch (buf[4]) {
394 case SNA_CMD_W:
395 trace_ds(" Write");
396 if (buflen > 5) {
397 ctlr_write(&buf[4], buflen-4, false);
398 } else {
399 trace_ds("\n");
400 }
401 break;
402 case SNA_CMD_EW:
403 trace_ds(" EraseWrite");
404 if (buflen > 5) {
405 ctlr_write(&buf[4], buflen-4, true);
406 } else {
407 trace_ds("\n");
408 }
409 break;
410 case SNA_CMD_EWA:
411 trace_ds(" EraseWriteAlternate");
412 if (buflen > 5) {
413 ctlr_write(&buf[4], buflen-4, true);
414 } else {
415 trace_ds("\n");
416 }
417 break;
418 case SNA_CMD_EAU:
419 trace_ds(" EraseAllUnprotected\n");
420 break;
421 default:
422 trace_ds(" unknown type 0x%02x\n", buf[4]);
423 return PDS_BAD_CMD;
424 }
425
426 return PDS_OKAY_NO_OUTPUT;
427 }
428
429 static void
query_reply_start(void)430 query_reply_start(void)
431 {
432 obptr = obuf;
433 space3270out(1);
434 *obptr++ = AID_SF;
435 qr_in_progress = true;
436 }
437
438 static void
do_query_reply(unsigned char code)439 do_query_reply(unsigned char code)
440 {
441 size_t len;
442 unsigned i;
443 const char *comma = "";
444 size_t obptr0 = obptr - obuf;
445 unsigned char *obptr_len;
446 unsigned short num, denom;
447
448 if (qr_in_progress) {
449 trace_ds("> StructuredField\n");
450 qr_in_progress = false;
451 }
452
453 space3270out(4);
454 obptr += 2; /* skip length for now */
455 *obptr++ = SFID_QREPLY;
456 *obptr++ = code;
457 switch (code) {
458
459 case QR_CHARSETS:
460 trace_ds("> QueryReply(CharacterSets)\n");
461 space3270out(64);
462 if (dbcs) {
463 *obptr++ = 0x8e; /* flags: GE, CGCSGID, DBCS */
464 } else {
465 *obptr++ = 0x82; /* flags: GE, CGCSGID present */
466 }
467 *obptr++ = 0x00; /* more flags */
468 *obptr++ = char_width; /* SDW */
469 *obptr++ = char_height; /* SDW */
470 *obptr++ = 0x00; /* no load PS */
471 *obptr++ = 0x00;
472 *obptr++ = 0x00;
473 *obptr++ = 0x00;
474 if (dbcs) {
475 *obptr++ = 0x0b; /* DL (11 bytes) */
476 } else {
477 *obptr++ = 0x07; /* DL (7 bytes) */
478 }
479
480 *obptr++ = 0x00; /* SET 0: */
481 if (dbcs) {
482 *obptr++ = 0x00; /* FLAGS: non-load, single- */
483 } /* plane, single-bute */
484 else {
485 *obptr++ = 0x10; /* FLAGS: non-loadable, */
486 } /* single-plane, single-byte,
487 no compare */
488 *obptr++ = 0x00; /* LCID 0 */
489 if (dbcs) {
490 *obptr++ = 0x00; /* SW 0 */
491 *obptr++ = 0x00; /* SH 0 */
492 *obptr++ = 0x00; /* SUBSN */
493 *obptr++ = 0x00; /* SUBSN */
494 }
495 SET32(obptr, cgcsgid); /* CGCSGID */
496 if (!standard_font) {
497 /* special 3270 font, includes APL */
498 *obptr++ = 0x01;/* SET 1: */
499 *obptr++ = 0x10;/* FLAGS: non-loadable, single-plane,
500 single-byte, no compare */
501 *obptr++ = 0xf1;/* LCID */
502 if (dbcs) {
503 *obptr++ = 0x00;/* SW 0 */
504 *obptr++ = 0x00;/* SH 0 */
505 *obptr++ = 0x00;/* SUBSN */
506 *obptr++ = 0x00;/* SUBSN */
507 }
508 *obptr++ = 0x03;/* CGCSGID: 3179-style APL2 */
509 *obptr++ = 0xc3;
510 *obptr++ = 0x01;
511 *obptr++ = 0x36;
512 }
513 if (dbcs) {
514 *obptr++ = 0x80; /* SET 0x80: */
515 *obptr++ = 0x20; /* FLAGS: DBCS */
516 *obptr++ = 0xf8; /* LCID: 0xf8 */
517 *obptr++ = char_width * 2; /* SW */
518 *obptr++ = char_height; /* SH */
519 *obptr++ = 0x41; /* SUBSN */
520 *obptr++ = 0x7f; /* SUBSN */
521 SET32(obptr, cgcsgid_dbcs); /* CGCSGID */
522 }
523 break;
524
525 case QR_IMP_PART:
526 trace_ds("> QueryReply(ImplicitPartition)\n");
527 space3270out(13);
528 *obptr++ = 0x0; /* reserved */
529 *obptr++ = 0x0;
530 *obptr++ = 0x0b; /* length of display size */
531 *obptr++ = 0x01; /* "implicit partition size" */
532 *obptr++ = 0x00; /* reserved */
533 SET16(obptr, 72); /* implicit partition width */
534 SET16(obptr, 66); /* implicit partition height */
535 SET16(obptr, maxCOLS); /* alternate height */
536 SET16(obptr, maxROWS); /* alternate width */
537 break;
538
539 case QR_NULL:
540 trace_ds("> QueryReply(Null)\n");
541 break;
542
543 case QR_SUMMARY:
544 trace_ds("> QueryReply(Summary(");
545 space3270out(NSR);
546 for (i = 0; i < NSR; i++) {
547 if (dbcs || supported_replies[i] != QR_DBCS_ASIA) {
548 trace_ds("%s%s", comma,
549 see_qcode(supported_replies[i]));
550 comma = ",";
551 *obptr++ = supported_replies[i];
552 }
553 }
554 trace_ds("))\n");
555 break;
556
557 case QR_USABLE_AREA:
558 trace_ds("> QueryReply(UsableArea)\n");
559 space3270out(19);
560 *obptr++ = 0x01; /* 12/14-bit addressing */
561 *obptr++ = 0x00; /* no special character features */
562 SET16(obptr, maxCOLS); /* usable width */
563 SET16(obptr, maxROWS); /* usable height */
564 *obptr++ = 0x01; /* units (mm) */
565 num = /*display_widthMM()*/ 8 * 5 / 4;
566 denom = /*display_width()*/ 7 * 72;
567 while (!(num % 2) && !(denom % 2)) {
568 num /= 2;
569 denom /= 2;
570 }
571 SET16(obptr, (int)num); /* Xr numerator */
572 SET16(obptr, (int)denom); /* Xr denominator */
573 num = /*display_heightMM()*/ 11 * 5 / 4;
574 denom = /*display_height()*/ 9 * 66;
575 while (!(num % 2) && !(denom % 2)) {
576 num /= 2;
577 denom /= 2;
578 }
579 SET16(obptr, (int)num); /* Yr numerator */
580 SET16(obptr, (int)denom); /* Yr denominator */
581 *obptr++ = char_width; /* AW */
582 *obptr++ = char_height; /* AH */
583 SET16(obptr, 0); /* buffer */
584 break;
585
586 case QR_COLOR:
587 trace_ds("> QueryReply(Color)\n");
588 space3270out(4 + 2*15);
589 *obptr++ = 0x00; /* no options */
590 *obptr++ = 16; /* report on 16 colors */
591 *obptr++ = 0x00; /* default color: */
592 *obptr++ = 0xf0 + HOST_COLOR_GREEN; /* green */
593 for (i = 0xf1; i <= 0xff; i++) {
594 *obptr++ = i;
595 *obptr++ = i;
596 }
597 break;
598
599 case QR_HIGHLIGHTING:
600 trace_ds("> QueryReply(Highlighting)\n");
601 space3270out(11);
602 *obptr++ = 5; /* report on 5 pairs */
603 *obptr++ = XAH_DEFAULT; /* default: */
604 *obptr++ = XAH_NORMAL; /* normal */
605 *obptr++ = XAH_BLINK; /* blink: */
606 *obptr++ = XAH_BLINK; /* blink */
607 *obptr++ = XAH_REVERSE; /* reverse: */
608 *obptr++ = XAH_REVERSE; /* reverse */
609 *obptr++ = XAH_UNDERSCORE; /* underscore: */
610 *obptr++ = XAH_UNDERSCORE; /* underscore */
611 *obptr++ = XAH_INTENSIFY; /* intensify: */
612 *obptr++ = XAH_INTENSIFY; /* intensify */
613 break;
614
615 case QR_REPLY_MODES:
616 trace_ds("> QueryReply(ReplyModes)\n");
617 space3270out(3);
618 *obptr++ = SF_SRM_FIELD;
619 *obptr++ = SF_SRM_XFIELD;
620 *obptr++ = SF_SRM_CHAR;
621 break;
622
623 case QR_DBCS_ASIA:
624 /* XXX: Should we support this, even when not in DBCS mode? */
625 trace_ds("> QueryReply(DbcsAsia)\n");
626 space3270out(7);
627 *obptr++ = 0x00; /* flags (none) */
628 *obptr++ = 0x03; /* field length 3 */
629 *obptr++ = 0x01; /* SI/SO supported */
630 *obptr++ = 0x80; /* character set ID 0x80 */
631 *obptr++ = 0x03; /* field length 3 */
632 *obptr++ = 0x02; /* input control */
633 *obptr++ = 0x01; /* creation supported */
634 break;
635
636 case QR_ALPHA_PART:
637 trace_ds("> QueryReply(AlphanumericPartitions)\n");
638 space3270out(4);
639 *obptr++ = 0; /* 1 partition */
640 SET16(obptr, maxROWS*maxCOLS); /* buffer space */
641 *obptr++ = 0; /* no special features */
642 break;
643
644 case QR_DDM:
645 trace_ds("> QueryReply(DistributedDataManagement)\n");
646 space3270out(8);
647 SET16(obptr,0); /* set reserved field to 0 */
648 SET16(obptr,2048); /* set inbound length limit */
649 SET16(obptr,2048); /* set outbound length limit */
650 SET16(obptr,0x0101); /* NSS=01, DDMSS=01 */
651 break;
652
653 default:
654 return; /* internal error */
655 }
656
657 obptr_len = obuf + obptr0;
658 len = (obptr - obuf) - obptr0;
659 SET16(obptr_len, len);
660 }
661
662 static void
query_reply_end(void)663 query_reply_end(void)
664 {
665 net_output();
666 }
667