-
Notifications
You must be signed in to change notification settings - Fork 42
/
console.c
3731 lines (3203 loc) · 126 KB
/
console.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* CONSOLE.C (c)Copyright Roger Bowler, 1999-2011 */
/* Hercules Console Device Handler */
/*-------------------------------------------------------------------*/
/* This module contains device handling functions for console */
/* devices for the Hercules ESA/390 emulator. */
/* */
/* Telnet support is provided for two classes of console device: */
/* - local non-SNA 3270 display consoles via tn3270 */
/* - local non-SNA 3270 printers via tn3270 */
/* - 1052 and 3215 console printer keyboards via regular telnet */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* This module also takes care of the differences between the */
/* remote 3270 and local non-SNA 3270 devices. In particular */
/* the support of command chaining, which is not supported on */
/* the remote 3270 implementation on which telnet 3270 is based. */
/* In the local non-SNA environment a chained read or write will */
/* continue at the buffer address where the previous command ended. */
/* In order to achieve this, this module will keep track of the */
/* buffer location, and adjust the buffer address on chained read */
/* and write operations. */
/* 03/06/00 Jan Jaeger. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Add code to bypass bug in telnet client from TCP/IP for OS/390 */
/* where telnet responds with the server response rather then the */
/* client response as documented in RFC1576. */
/* 20/06/00 Jan Jaeger. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Corrections to buffer position calculations in find_buffer_pos */
/* and get_screen_pos subroutines (symptom: message IEE305I) */
/* 09/12/00 Roger Bowler. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* When using VTAM with local non-SNA 3270 devices, ensure that */
/* enough bufferspace is available when doing IND$FILE type */
/* filetransfers. Code IOBUF=(,3992) in ATCSTRxx, and/or BUFNUM=xxx */
/* on the LBUILD LOCAL statement defining the 3270 device. JJ */
/* */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Ignore "Negotiate About Window Size" client option (for now) so */
/* WinNT version of telnet works. -- Greg Price (implemted by Fish) */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#include "hercules.h"
#include "devtype.h"
#include "opcode.h"
#include "sr.h"
#if defined(WIN32) && defined(OPTION_DYNAMIC_LOAD) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
SYSBLK *psysblk;
#define sysblk (*psysblk)
#define config_cnslport (*config_cnslport)
static
#else
#if defined(OPTION_DYNAMIC_LOAD) && defined(_MSVC_)
DLL_IMPORT
#else
extern
#endif
#endif
char *config_cnslport;
/*-------------------------------------------------------------------*/
/* Ivan Warren 20040227 */
/* This table is used by channel.c to determine if a CCW code is an */
/* immediate command or not */
/* The tape is addressed in the DEVHND structure as 'DEVIMM immed' */
/* 0 : Command is NOT an immediate command */
/* 1 : Command is an immediate command */
/* Note : An immediate command is defined as a command which returns */
/* CE (channel end) during initialisation (that is, no data is */
/* actually transfered. In this case, IL is not indicated for a CCW */
/* Format 0 or for a CCW Format 1 when IL Suppression Mode is in */
/* effect */
/*-------------------------------------------------------------------*/
static BYTE constty_immed[256]=
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
{ 0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0, /* 00 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 10 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 30 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* A0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* B0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* C0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* D0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* E0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* F0 */
static BYTE loc3270_immed[256]=
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
{ 0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1, /* 00 */
0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, /* 10 */
0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, /* 20 */
0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, /* 30 */
0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, /* 40 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* A0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* B0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* C0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* D0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* E0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* F0 */
/*-------------------------------------------------------------------*/
/* Telnet command definitions */
/*-------------------------------------------------------------------*/
#define BINARY 0 /* Binary Transmission */
#define IS 0 /* Used by terminal-type negotiation */
#define SEND 1 /* Used by terminal-type negotiation */
#define ECHO_OPTION 1 /* Echo option */
#define SUPPRESS_GA 3 /* Suppress go-ahead option */
#define TIMING_MARK 6 /* Timing mark option */
#define TERMINAL_TYPE 24 /* Terminal type option */
#define NAWS 31 /* Negotiate About Window Size */
#define EOR 25 /* End of record option */
#define EOR_MARK 239 /* End of record marker */
#define SE 240 /* End of subnegotiation parameters */
#define NOP 241 /* No operation */
#define DATA_MARK 242 /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification */
#define BRK 243 /* Break character */
#define IP 244 /* Interrupt Process */
#define AO 245 /* Abort Output */
#define AYT 246 /* Are You There */
#define EC 247 /* Erase character */
#define EL 248 /* Erase Line */
#define GA 249 /* Go ahead */
#define SB 250 /* Subnegotiation of indicated option */
#define WILL 251 /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option */
#define WONT 252 /* Indicates the refusal to perform,
or continue performing, the
indicated option */
#define DO 253 /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option */
#define DONT 254 /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option */
#define IAC 255 /* Interpret as Command */
/*-------------------------------------------------------------------*/
/* 3270 definitions */
/*-------------------------------------------------------------------*/
/* 3270 local commands (CCWs) */
#define L3270_EAU 0x0F /* Erase All Unprotected */
#define L3270_EW 0x05 /* Erase/Write */
#define L3270_EWA 0x0D /* Erase/Write Alternate */
#define L3270_RB 0x02 /* Read Buffer */
#define L3270_RM 0x06 /* Read Modified */
#define L3270_WRT 0x01 /* Write */
#define L3270_WSF 0x11 /* Write Structured Field */
#define L3270_NOP 0x03 /* No Operation */
#define L3270_SELRM 0x0B /* Select RM */
#define L3270_SELRB 0x1B /* Select RB */
#define L3270_SELRMP 0x2B /* Select RMP */
#define L3270_SELRBP 0x3B /* Select RBP */
#define L3270_SELWRT 0x4B /* Select WRT */
#define L3270_SENSE 0x04 /* Sense */
#define L3270_SENSEID 0xE4 /* Sense ID */
/* 3270 remote commands */
#define R3270_EAU 0x6F /* Erase All Unprotected */
#define R3270_EW 0xF5 /* Erase/Write */
#define R3270_EWA 0x7E /* Erase/Write Alternate */
#define R3270_RB 0xF2 /* Read Buffer */
#define R3270_RM 0xF6 /* Read Modified */
#define R3270_RMA 0x6E /* Read Modified All */
#define R3270_WRT 0xF1 /* Write */
#define R3270_WSF 0xF3 /* Write Structured Field */
/* 3270 orders */
#define O3270_SBA 0x11 /* Set Buffer Address */
#define O3270_SF 0x1D /* Start Field */
#define O3270_SFE 0x29 /* Start Field Extended */
#define O3270_SA 0x28 /* Set Attribute */
#define O3270_IC 0x13 /* Insert Cursor */
#define O3270_MF 0x2C /* Modify Field */
#define O3270_PT 0x05 /* Program Tab */
#define O3270_RA 0x3C /* Repeat to Address */
#define O3270_EUA 0x12 /* Erase Unprotected to Addr */
#define O3270_GE 0x08 /* Graphic Escape */
/* Inbound structured fields */
#define SF3270_AID 0x88 /* Aid value of inbound SF */
#define SF3270_3270DS 0x80 /* SFID of 3270 datastream SF*/
/* 12 bit 3270 buffer address code conversion table */
static BYTE sba_code[] = { "\x40\xC1\xC2\xC3\xC4\xC5\xC6\xC7"
"\xC8\xC9\x4A\x4B\x4C\x4D\x4E\x4F"
"\x50\xD1\xD2\xD3\xD4\xD5\xD6\xD7"
"\xD8\xD9\x5A\x5B\x5C\x5D\x5E\x5F"
"\x60\x61\xE2\xE3\xE4\xE5\xE6\xE7"
"\xE8\xE9\x6A\x6B\x6C\x6D\x6E\x6F"
"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7"
"\xF8\xF9\x7A\x7B\x7C\x7D\x7E\x7F" };
/*-------------------------------------------------------------------*/
/* Internal macro definitions */
/*-------------------------------------------------------------------*/
/* DEBUG_LVL: 0 = none
1 = status
2 = headers
3 = buffers
*/
#define DEBUG_LVL 0
#if DEBUG_LVL == 0
#define TNSDEBUG1 1 ? ((void)0) : logmsg
#define TNSDEBUG2 1 ? ((void)0) : logmsg
#define TNSDEBUG3 1 ? ((void)0) : logmsg
#endif
#if DEBUG_LVL == 1
#define TNSDEBUG1 logmsg
#define TNSDEBUG2 1 ? ((void)0) : logmsg
#define TNSDEBUG3 1 ? ((void)0) : logmsg
#endif
#if DEBUG_LVL == 2
#define TNSDEBUG1 logmsg
#define TNSDEBUG2 logmsg
#define TNSDEBUG3 1 ? ((void)0) : logmsg
#endif
#if DEBUG_LVL == 3
#define TNSDEBUG1 logmsg
#define TNSDEBUG2 logmsg
#define TNSDEBUG3 logmsg
#endif
#define TNSERROR logmsg
#define BUFLEN_3270 65536 /* 3270 Send/Receive buffer */
#define BUFLEN_1052 150 /* 1052 Send/Receive buffer */
#undef FIX_QWS_BUG_FOR_MCS_CONSOLES
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
static HOST_INFO cons_hostinfo; /* Host info for this system */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO TRACE THE CONTENTS OF AN ASCII MESSAGE PACKET */
/*-------------------------------------------------------------------*/
#if DEBUG_LVL == 3
static void
packet_trace(BYTE *addr, int len)
{
int i, offset;
BYTE c;
BYTE print_chars[17];
for (offset=0; offset < len; )
{
memset(print_chars,0,sizeof(print_chars));
logmsg("+%4.4X ", offset);
for (i=0; i < 16; i++)
{
c = *addr++;
if (offset < len) {
logmsg("%2.2X", c);
print_chars[i] = '.';
if (isprint(c)) print_chars[i] = c;
c = guest_to_host(c);
if (isprint(c)) print_chars[i] = c;
}
else {
logmsg(" ");
}
offset++;
if ((offset & 3) == 0) {
logmsg(" ");
}
} /* end for(i) */
logmsg(" %s\n", print_chars);
} /* end for(offset) */
} /* end function packet_trace */
#else
#define packet_trace( _addr, _len)
#endif
#if 1
struct sockaddr_in * get_inet_socket(char *host_serv)
{
char *host = NULL;
char *serv;
struct sockaddr_in *sin;
if((serv = strchr(host_serv,':')))
{
*serv++ = '\0';
if(*host_serv)
host = host_serv;
}
else
serv = host_serv;
if(!(sin = malloc(sizeof(struct sockaddr_in))))
return sin;
sin->sin_family = AF_INET;
if(host)
{
struct hostent *hostent;
hostent = gethostbyname(host);
if(!hostent)
{
logmsg(_("HHCGI001I Unable to determine IP address from %s\n"),
host);
free(sin);
return NULL;
}
memcpy(&sin->sin_addr,*hostent->h_addr_list,sizeof(sin->sin_addr));
}
else
sin->sin_addr.s_addr = INADDR_ANY;
if(serv)
{
if(!isdigit(*serv))
{
struct servent *servent;
servent = getservbyname(serv, "tcp");
if(!servent)
{
logmsg(_("HHCGI002I Unable to determine port number from %s\n"),
host);
free(sin);
return NULL;
}
sin->sin_port = servent->s_port;
}
else
sin->sin_port = htons(atoi(serv));
}
else
{
logmsg(_("HHCGI003E Invalid parameter: %s\n"),
host_serv);
free(sin);
return NULL;
}
return sin;
}
#endif
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO REMOVE ANY IAC SEQUENCES FROM THE DATA STREAM */
/* Returns the new length after deleting IAC commands */
/*-------------------------------------------------------------------*/
static int
remove_iac (BYTE *buf, int len)
{
int m, n, c;
for (m=0, n=0; m < len; ) {
/* Interpret IAC commands */
if (buf[m] == IAC) {
/* Treat IAC in last byte of buffer as IAC NOP */
c = (++m < len)? buf[m++] : NOP;
/* Process IAC command */
switch (c) {
case IAC: /* Insert single IAC in buffer */
buf[n++] = IAC;
break;
case BRK: /* Set ATTN indicator */
break;
case IP: /* Set SYSREQ indicator */
break;
case WILL: /* Skip option negotiation command */
case WONT:
case DO:
case DONT:
m++;
break;
case SB: /* Skip until IAC SE sequence found */
for (; m < len; m++) {
if (buf[m] != IAC) continue;
if (++m >= len) break;
if (buf[m] == SE) { m++; break; }
} /* end for */
default: /* Ignore NOP or unknown command */
break;
} /* end switch(c) */
} else {
/* Copy data bytes */
if (n < m) buf[n] = buf[m];
m++; n++;
}
} /* end for */
if (n < m) {
TNSDEBUG3("console: DBG001: %d IAC bytes removed, newlen=%d\n",
m-n, n);
packet_trace (buf, n);
}
return n;
} /* end function remove_iac */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO DOUBLE UP ANY IAC BYTES IN THE DATA STREAM */
/* Returns the new length after inserting extra IAC bytes */
/*-------------------------------------------------------------------*/
static int
double_up_iac (BYTE *buf, int len)
{
int m, n, x, newlen;
/* Count the number of IAC bytes in the data */
for (x=0, n=0; n < len; n++)
if (buf[n] == IAC) x++;
/* Exit if nothing to do */
if (x == 0) return len;
/* Insert extra IAC bytes backwards from the end of the buffer */
newlen = len + x;
TNSDEBUG3("console: DBG002: %d IAC bytes added, newlen=%d\n",
x, newlen);
for (n=newlen, m=len; n > m; ) {
buf[--n] = buf[--m];
if (buf[n] == IAC) buf[--n] = IAC;
}
packet_trace (buf, newlen);
return newlen;
} /* end function double_up_iac */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO TRANSLATE A NULL-TERMINATED STRING TO EBCDIC */
/*-------------------------------------------------------------------*/
static BYTE *
translate_to_ebcdic (char *str)
{
int i; /* Array subscript */
BYTE c; /* Character work area */
for (i = 0; str[i] != '\0'; i++)
{
c = str[i];
str[i] = (isprint(c) ? host_to_guest(c) : SPACE);
}
return (BYTE *)str;
} /* end function translate_to_ebcdic */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO SEND A DATA PACKET TO THE CLIENT */
/*-------------------------------------------------------------------*/
static int
send_packet (int csock, BYTE *buf, int len, char *caption)
{
int rc; /* Return code */
if (caption != NULL) {
TNSDEBUG2("console: DBG003: Sending %s\n", caption);
packet_trace (buf, len);
}
rc = send (csock, buf, len, 0);
if (rc < 0) {
TNSERROR("console: DBG021: send: %s\n", strerror(HSO_errno));
return -1;
} /* end if(rc) */
return 0;
} /* end function send_packet */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO RECEIVE A DATA PACKET FROM THE CLIENT */
/* This subroutine receives bytes from the client. It stops when */
/* the receive buffer is full, or when the last two bytes received */
/* consist of the IAC character followed by a specified delimiter. */
/* If zero bytes are received, this means the client has closed the */
/* connection, and this is treated as an error. */
/* Input: */
/* csock is the socket number */
/* buf points to area to receive data */
/* reqlen is the number of bytes requested */
/* delim is the delimiter character (0=no delimiter) */
/* Output: */
/* buf is updated with data received */
/* The return value is the number of bytes received, or */
/* -1 if an error occurred. */
/*-------------------------------------------------------------------*/
static int
recv_packet (int csock, BYTE *buf, int reqlen, BYTE delim)
{
int rc=0; /* Return code */
int rcvlen=0; /* Length of data received */
while (rcvlen < reqlen) {
rc = recv (csock, buf + rcvlen, reqlen - rcvlen, 0);
if (rc < 0) {
TNSERROR("console: DBG022: recv: %s\n", strerror(HSO_errno));
return -1;
}
if (rc == 0) {
TNSDEBUG1("console: DBG004: Connection closed by client\n");
return -1;
}
rcvlen += rc;
if (delim != '\0' && rcvlen >= 2
&& buf[rcvlen-2] == IAC && buf[rcvlen-1] == delim)
break;
}
TNSDEBUG2("console: DBG005: Packet received length=%d\n", rcvlen);
packet_trace (buf, rcvlen);
return rcvlen;
} /* end function recv_packet */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO RECEIVE A PACKET AND COMPARE WITH EXPECTED VALUE */
/*-------------------------------------------------------------------*/
static int
expect (int csock, BYTE *expected, int len, char *caption)
{
int rc; /* Return code */
BYTE buf[512]; /* Receive buffer */
#if 1
/* TCP/IP for MVS returns the server sequence rather then
the client sequence during bin negotiation 19/06/00 Jan Jaeger */
static BYTE do_bin[] = { IAC, DO, BINARY, IAC, WILL, BINARY };
static BYTE will_bin[] = { IAC, WILL, BINARY, IAC, DO, BINARY };
#endif
UNREFERENCED(caption);
rc = recv_packet (csock, buf, len, 0);
if (rc < 0) return -1;
#if 1
/* TCP/IP FOR MVS DOES NOT COMPLY TO RFC 1576 THIS IS A BYPASS */
if(memcmp(buf, expected, len) != 0
&& !(len == sizeof(will_bin)
&& memcmp(expected, will_bin, len) == 0
&& memcmp(buf, do_bin, len) == 0) )
#else
if (memcmp(buf, expected, len) != 0)
#endif
{
TNSDEBUG2("console: DBG006: Expected %s\n", caption);
return -1;
}
TNSDEBUG2("console: DBG007: Received %s\n", caption);
return 0;
} /* end function expect */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO NEGOTIATE TELNET PARAMETERS */
/* This subroutine negotiates the terminal type with the client */
/* and uses the terminal type to determine whether the client */
/* is to be supported as a 3270 display console or as a 1052/3215 */
/* printer-keyboard console. */
/* */
/* Valid display terminal types are "IBM-NNNN", "IBM-NNNN-M", and */
/* "IBM-NNNN-M-E", where NNNN is 3270, 3277, 3278, 3279, 3178, 3179, */
/* or 3180, M indicates the screen size (2=25x80, 3=32x80, 4=43x80, */
/* 5=27x132, X=determined by Read Partition Query command), and */
/* -E is an optional suffix indicating that the terminal supports */
/* extended attributes. Displays are negotiated into tn3270 mode. */
/* An optional device number suffix (example: IBM-3270@01F) may */
/* be specified to request allocation to a specific device number. */
/* Valid 3270 printer type is "IBM-3287-1" */
/* */
/* Terminal types whose first four characters are not "IBM-" are */
/* handled as printer-keyboard consoles using telnet line mode. */
/* */
/* Input: */
/* csock Socket number for client connection */
/* Output: */
/* class D=3270 display console, K=printer-keyboard console */
/* P=3270 printer */
/* model 3270 model indicator (2,3,4,5,X) */
/* extatr 3270 extended attributes (Y,N) */
/* devn Requested device number, or FFFF=any device number */
/* Return value: */
/* 0=negotiation successful, -1=negotiation error */
/*-------------------------------------------------------------------*/
static int
negotiate(int csock, BYTE *class, BYTE *model, BYTE *extatr, U16 *devn,char *group)
{
int rc; /* Return code */
char *termtype; /* Pointer to terminal type */
char *s; /* String pointer */
BYTE c; /* Trailing character */
U16 devnum; /* Requested device number */
BYTE buf[512]; /* Telnet negotiation buffer */
static BYTE do_term[] = { IAC, DO, TERMINAL_TYPE };
static BYTE will_term[] = { IAC, WILL, TERMINAL_TYPE };
static BYTE req_type[] = { IAC, SB, TERMINAL_TYPE, SEND, IAC, SE };
static BYTE type_is[] = { IAC, SB, TERMINAL_TYPE, IS };
static BYTE do_eor[] = { IAC, DO, EOR, IAC, WILL, EOR };
static BYTE will_eor[] = { IAC, WILL, EOR, IAC, DO, EOR };
static BYTE do_bin[] = { IAC, DO, BINARY, IAC, WILL, BINARY };
static BYTE will_bin[] = { IAC, WILL, BINARY, IAC, DO, BINARY };
#if 0
static BYTE do_tmark[] = { IAC, DO, TIMING_MARK };
static BYTE will_tmark[] = { IAC, WILL, TIMING_MARK };
static BYTE wont_sga[] = { IAC, WONT, SUPPRESS_GA };
static BYTE dont_sga[] = { IAC, DONT, SUPPRESS_GA };
#endif
static BYTE wont_echo[] = { IAC, WONT, ECHO_OPTION };
static BYTE dont_echo[] = { IAC, DONT, ECHO_OPTION };
static BYTE will_naws[] = { IAC, WILL, NAWS };
/* Perform terminal-type negotiation */
rc = send_packet (csock, do_term, sizeof(do_term),
"IAC DO TERMINAL_TYPE");
if (rc < 0) return -1;
rc = expect (csock, will_term, sizeof(will_term),
"IAC WILL TERMINAL_TYPE");
if (rc < 0) return -1;
/* Request terminal type */
rc = send_packet (csock, req_type, sizeof(req_type),
"IAC SB TERMINAL_TYPE SEND IAC SE");
if (rc < 0) return -1;
rc = recv_packet (csock, buf, sizeof(buf)-2, SE);
if (rc < 0) return -1;
/* Ignore Negotiate About Window Size */
if (rc >= (int)sizeof(will_naws) &&
memcmp (buf, will_naws, sizeof(will_naws)) == 0)
{
memmove(buf, &buf[sizeof(will_naws)], (rc - sizeof(will_naws)));
rc -= sizeof(will_naws);
}
if (rc < (int)(sizeof(type_is) + 2)
|| memcmp(buf, type_is, sizeof(type_is)) != 0
|| buf[rc-2] != IAC || buf[rc-1] != SE) {
TNSDEBUG2("console: DBG008: Expected IAC SB TERMINAL_TYPE IS\n");
return -1;
}
buf[rc-2] = '\0';
termtype = (char *)(buf + sizeof(type_is));
TNSDEBUG2("console: DBG009: Received IAC SB TERMINAL_TYPE IS %s IAC SE\n",
termtype);
/* Check terminal type string for device name suffix */
s = strchr (termtype, '@');
if(s!=NULL)
{
if(strlen(s)<16)
{
strlcpy(group,&s[1],16);
}
}
else
{
group[0]=0;
}
if (s != NULL && sscanf (s, "@%hx%c", &devnum,&c) == 1)
{
*devn = devnum;
group[0]=0;
}
else
{
*devn = 0xFFFF;
}
/* Test for non-display terminal type */
if (memcmp(termtype, "IBM-", 4) != 0)
{
#if 0
/* Perform line mode negotiation */
rc = send_packet (csock, do_tmark, sizeof(do_tmark),
"IAC DO TIMING_MARK");
if (rc < 0) return -1;
rc = expect (csock, will_tmark, sizeof(will_tmark),
"IAC WILL TIMING_MARK");
if (rc < 0) return 0;
rc = send_packet (csock, wont_sga, sizeof(wont_sga),
"IAC WONT SUPPRESS_GA");
if (rc < 0) return -1;
rc = expect (csock, dont_sga, sizeof(dont_sga),
"IAC DONT SUPPRESS_GA");
if (rc < 0) return -1;
#endif
if (memcmp(termtype, "ANSI", 4) == 0)
{
rc = send_packet (csock, wont_echo, sizeof(wont_echo),
"IAC WONT ECHO");
if (rc < 0) return -1;
rc = expect (csock, dont_echo, sizeof(dont_echo),
"IAC DONT ECHO");
if (rc < 0) return -1;
}
/* Return printer-keyboard terminal class */
*class = 'K';
*model = '-';
*extatr = '-';
return 0;
}
/* Determine display terminal model */
if (memcmp(termtype+4,"DYNAMIC",7) == 0) {
*model = 'X';
*extatr = 'Y';
} else {
if (!(memcmp(termtype+4, "3277", 4) == 0
|| memcmp(termtype+4, "3270", 4) == 0
|| memcmp(termtype+4, "3178", 4) == 0
|| memcmp(termtype+4, "3278", 4) == 0
|| memcmp(termtype+4, "3179", 4) == 0
|| memcmp(termtype+4, "3180", 4) == 0
|| memcmp(termtype+4, "3287", 4) == 0
|| memcmp(termtype+4, "3279", 4) == 0))
return -1;
*model = '2';
*extatr = 'N';
if (termtype[8]=='-') {
if (termtype[9] < '1' || termtype[9] > '5')
return -1;
*model = termtype[9];
if (memcmp(termtype+4, "328",3) == 0) *model = '2';
if (memcmp(termtype+10, "-E", 2) == 0)
*extatr = 'Y';
}
}
/* Perform end-of-record negotiation */
rc = send_packet (csock, do_eor, sizeof(do_eor),
"IAC DO EOR IAC WILL EOR");
if (rc < 0) return -1;
rc = expect (csock, will_eor, sizeof(will_eor),
"IAC WILL EOR IAC DO EOR");
if (rc < 0) return -1;
/* Perform binary negotiation */
rc = send_packet (csock, do_bin, sizeof(do_bin),
"IAC DO BINARY IAC WILL BINARY");
if (rc < 0) return -1;
rc = expect (csock, will_bin, sizeof(will_bin),
"IAC WILL BINARY IAC DO BINARY");
if (rc < 0) return -1;
/* Return display terminal class */
if (memcmp(termtype+4,"3287",4)==0) *class='P';
else *class = 'D';
return 0;
} /* end function negotiate */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO RECEIVE 3270 DATA FROM THE CLIENT */
/* This subroutine receives bytes from the client and appends them */
/* to any data already in the 3270 receive buffer. */
/* If zero bytes are received, this means the client has closed the */
/* connection, and attention and unit check status is returned. */
/* If the buffer is filled before receiving end of record, then */
/* attention and unit check status is returned. */
/* If the data ends with IAC followed by EOR_MARK, then the data */
/* is scanned to remove any IAC sequences, attention status is */
/* returned, and the read pending indicator is set. */
/* If the data accumulated in the buffer does not yet constitute a */
/* complete record, then zero status is returned, and a further */
/* call must be made to this subroutine when more data is available. */
/*-------------------------------------------------------------------*/
static BYTE
recv_3270_data (DEVBLK *dev)
{
int rc; /* Return code */
int eor = 0; /* 1=End of record received */
/* If there is a complete data record already in the buffer
then discard it before reading more data */
if (dev->readpending)
{
dev->rlen3270 = 0;
dev->readpending = 0;
}
/*
The following chunk of code was added to try and catch
a race condition that may or may no longer still exist.
*/
TNSDEBUG1("console: DBG031: verifying data is available...\n");
{
fd_set readset;
struct timeval tv = {0,0}; /* (non-blocking poll) */
FD_ZERO( &readset );
FD_SET( dev->fd, &readset );
while ( (rc = select ( dev->fd+1, &readset, NULL, NULL, &tv )) < 0
&& HSO_EINTR == HSO_errno )
; /* NOP (keep retrying if EINTR) */
if (rc < 0)
{
TNSERROR("console: DBG032: select failed: %s\n", strerror(HSO_errno));
return 0;
}
ASSERT(rc <= 1);
if (!FD_ISSET(dev->fd, &readset))
{
ASSERT(rc == 0);
TNSDEBUG1("console: DBG033: no data available; returning 0...\n");
return 0;
}
ASSERT(rc == 1);
}
TNSDEBUG1("console: DBG034: data IS available; attempting recv...\n");
/* Receive bytes from client */
rc = recv (dev->fd, dev->buf + dev->rlen3270,
BUFLEN_3270 - dev->rlen3270, 0);
if (rc < 0) {
if ( HSO_ECONNRESET == HSO_errno )
logmsg( _( "HHCTE014I %4.4X device %4.4X client %s connection reset\n" ),
dev->devtype, dev->devnum, inet_ntoa(dev->ipaddr) );
else
TNSERROR("console: DBG023: recv: %s\n", strerror(HSO_errno));
dev->sense[0] = SENSE_EC;
return (CSW_ATTN | CSW_UC);
}
/* If zero bytes were received then client has closed connection */
if (rc == 0) {
logmsg (_("HHCTE007I %4.4X device %4.4X client %s connection closed\n"),
dev->devtype, dev->devnum, inet_ntoa(dev->ipaddr));
dev->sense[0] = SENSE_IR;
return (CSW_ATTN | CSW_UC | CSW_DE);
}
/* Update number of bytes in receive buffer */
dev->rlen3270 += rc;
/* Check whether Attn indicator was received */
if (dev->rlen3270 >= 2
&& dev->buf[dev->rlen3270 - 2] == IAC
&& dev->buf[dev->rlen3270 - 1] == BRK)
eor = 1;
/* Check whether SysRq indicator was received */
if (dev->rlen3270 >= 2
&& dev->buf[dev->rlen3270 - 2] == IAC
&& dev->buf[dev->rlen3270 - 1] == IP)
eor = 1;
/* Check whether end of record marker was received */
if (dev->rlen3270 >= 2
&& dev->buf[dev->rlen3270 - 2] == IAC
&& dev->buf[dev->rlen3270 - 1] == EOR_MARK)
eor = 1;
/* If record is incomplete, test for buffer full */
if (eor == 0 && dev->rlen3270 >= BUFLEN_3270)
{
TNSDEBUG1("console: DBG010: 3270 buffer overflow\n");
dev->sense[0] = SENSE_DC;
return (CSW_ATTN | CSW_UC);
}
/* Return zero status if record is incomplete */
if (eor == 0)
return 0;
/* Trace the complete 3270 data packet */
TNSDEBUG2("console: DBG011: Packet received length=%d\n",
dev->rlen3270);
packet_trace (dev->buf, dev->rlen3270);
/* Strip off the telnet EOR marker */
dev->rlen3270 -= 2;
/* Remove any embedded IAC commands */
dev->rlen3270 = remove_iac (dev->buf, dev->rlen3270);
/* Set the read pending indicator and return attention status */
dev->readpending = 1;
return (CSW_ATTN);
} /* end function recv_3270_data */
/*-------------------------------------------------------------------*/
/* SUBROUTINE TO SOLICIT 3270 DATA FROM THE CLIENT */
/* This subroutine sends a Read or Read Modified command to the */
/* client and then receives the data into the 3270 receive buffer. */
/* This subroutine is called by loc3270_execute_ccw as a result of */
/* processing a Read Buffer CCW, or a Read Modified CCW when no */
/* data is waiting in the 3270 read buffer. It waits until the */
/* client sends end of record. Certain tn3270 clients fail to */
/* flush their buffer until the user presses an attention key; */
/* these clients cause this routine to hang and are not supported. */
/* Since this routine is only called while a channel program is */
/* active on the device, we can rely on the dev->busy flag to */
/* prevent the connection thread from issuing a read and capturing */
/* the incoming data intended for this routine. */
/* The caller MUST hold the device lock. */
/* Returns zero status if successful, or unit check if error. */
/*-------------------------------------------------------------------*/
static BYTE
solicit_3270_data (DEVBLK *dev, BYTE cmd)
{
int rc; /* Return code */
int len; /* Data length */
BYTE buf[32]; /* tn3270 write buffer */
/* Clear the inbound buffer of any unsolicited
data accumulated by the connection thread */
dev->rlen3270 = 0;
dev->readpending = 0;
/* Construct a 3270 read command in the outbound buffer */
len = 0;
buf[len++] = cmd;
/* Append telnet EOR marker to outbound buffer */
buf[len++] = IAC;
buf[len++] = EOR_MARK;
/* Send the 3270 read command to the client */