-
Notifications
You must be signed in to change notification settings - Fork 7
/
protocol.c
1524 lines (1362 loc) · 49.1 KB
/
protocol.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
/* PROTOCOL.C V4.5-mea1.1
| Copyright (c) 1988,1989,1990,1991,1992 by
| The Hebrew University of Jerusalem, Computation Center.
|
| This software is distributed under a license from the Hebrew University
| of Jerusalem. It may be copied only under the terms listed in the license
| agreement. This copyright message should never be changed or removed.
| This software is gievn without any warranty, and the Hebrew University
| of Jerusalem assumes no responsibility for any damage that might be caused
| by use or misuse of this software.
|
| Do the protocol handling.
| The IoLines.SubState Should change in the future to per-stream instead of
| per-channel.
| FCS bits are not checked. Only the wait-a-bit is checked. When we get such
| thing, a log at level 1 is produced. Change this log level to be 2 after
| this module is debugged.
|
| The function that deblocks incoming records assumes for some types that they
| are a single-record block.
|
| MORE things to do:
| 3. Statistics computation: Count Null blocks as ACKS also.
| 5. Handle prepare mode on idle line.
| mea1.1 - 10-Oct-93 - Removed all conditional compilation of HUJI vs. FUNET
| version. It is now FUNET version only..
|
| Document:
| Network Job Entry - Formats and Protocols (IBM)
| SC23-0070-02
| GG22-9373-02 (older version)
*/
#include "consts.h"
#include "headers.h"
#include "prototypes.h"
static void handle_text_block __(( const int Index, void *buffer, int size ));
static void input_read_error __(( const int Index, const int status, struct LINE *Line ));
static void handle_nak __(( const int Index ));
static void handle_enq __(( const int Index ));
static void fill_message_buffer __(( const int Index, struct LINE *Line ));
static int request_to_send_file __(( const int Index, struct LINE *Line ));
static void income_file_request __(( const int Index, struct LINE *Line, const unsigned char *StreamNumberP ));
static void send_signon __(( const int Index, const int flag ));
static void inform_users_about_line __(( const int Index, const int PreviousState ));
EXTERNAL struct NEGATIVE_ACK NegativeAck;
EXTERNAL struct POSITIVE_ACK PositiveAck;
EXTERNAL struct ENQUIRE Enquire;
EXTERNAL struct SIGNON InitialSignon, ResponseSignon;
EXTERNAL struct SIGN_OFF SignOff;
EXTERNAL struct EOF_BLOCK EOFblock;
EXTERNAL struct PERMIT_FILE PermitFile;
EXTERNAL struct REJECT_FILE RejectFile;
EXTERNAL int MustShutDown;
#define SS_ABORT 44 /* VMS Abort code */
#define SS_TIMEOUT 556
#define INITIAL_SIGNON 1 /* For send_singon routine */
#define FINAL_SIGNON 2
#define EXPLICIT_ACK 0 /* ACK was received */
#define IMPLICIT_ACK 1 /* DLE-STX block also acks */
#define INPLICIT_ACK 2 /* Idle line - delayed ack */
/* A macro to send either DEL+ACK0 or an empty block as ACK */
unsigned char NullBuffer = 0;
/* Send an ACK or Null-buffer, depending on the line's state. If the line is
of TCP type - don't send ACks of any type, since this is a reliable link,
and no ACKS are expected. DECnet expects ACKs, since it has problems working
full duplex.
*/
#define SEND_ACK() { \
if (((Line->flags & F_RELIABLE) == 0) || /* These have to get ACK always */ \
(Line->state != ACTIVE)) { \
if (((Line->flags & F_ACK_NULLS) == 0) || /* Use normal ACK */ \
(Line->state != ACTIVE)) \
send_data(Index, &PositiveAck, sizeof(struct POSITIVE_ACK), \
SEND_AS_IS); \
else \
send_data(Index, &NullBuffer, 1, ADD_BCB_CRC); \
} \
}
/*
| Try recovery from error. If there are only few errors on the line, try
| sending NAK. If there are too much errors - restart the line.
| Two error counters are kept: TotalError which counts all errors on the
| line since program startup, and Errors which counts the number of errors
| since the last seccessfull operation on the line.
*/
static void
error_retry(Index, Line)
const int Index;
struct LINE *Line;
{
Line->TotalErrors++; /* Count total errors on line */
Line->stats.RetriesOut += 1;
if ((Line->errors++) < MAX_ERRORS) { /* Not too much errors */
send_data(Index, &NegativeAck, sizeof(struct NEGATIVE_ACK),
SEND_AS_IS);
return;
}
else { /* Too many errors. Restart the line */
logger(1, "PROTOCOL: Too many error on line %d, restarting.\n",
Index);
restart_channel(Index);
return;
}
}
/*
| Restart a channel - I.e.: close files, delete output files.
| If there are interactive messages/commands waiting on the line,
| clear them, and free their memory.
| If there is a file in transit, change its state to waiting.
*/
void
restart_channel(Index)
const int Index;
{
struct LINE *Line;
/* struct MESSAGE *MessageEntry; */
register long PreviousState; /* To know whether to inform
state change */
register int i; /* Stream index */
extern void abort_streams_and_requeue();
Line = &(IoLines[Index]);
if (Index > MAX_LINES || Line->HostName[0] == 0) {
logger(1,"PROTOCOL: restart_channel(%d) - Bad line number!\n",Index);
return;
}
/* Init_link_state() will reset the line TCP line */
/* init_link_state(Index); --- NOT IN USE! */
abort_streams_and_requeue(Index); /* core of init_link_state() */
/* Send the ENQ block again, only if the line was before in
some active state. If it wasn't, then this call is
because we just want to close the files. If the line is
of type TCP, we close it in any case (put it in INACTIVE
state). We do not do it only during the initial signon
since NAKs are exchanged there. */
PreviousState = Line->state;
if ((Line->flags & F_RELIABLE) && (Line->state != DRAIN))
Line->state = TCP_SYNC; /* This will cause it to call close_line */
switch (Line->state) {
case DRAIN:
case I_SIGNON_SENT:
case F_SIGNON_SENT:
case ACTIVE: /* Restart it and put in DRAIN mode */
Line->state = DRAIN;
if (Line->socket >= 0)
send_data(Index, &Enquire, sizeof(struct ENQUIRE), SEND_AS_IS);
/* Inform registered users about line being disabled */
if (InformUsersCount > 0) {
if (Line->state != PreviousState) {
inform_users_about_line(Index, PreviousState);
}
}
break;
case TCP_SYNC: /* Line inactive */
case SIGNOFF:
Line->state = INACTIVE;
close_line(Index); /* Line is disabled, so close it */
case INACTIVE: /* On all the other types,
the channel is closed */
case LISTEN:
case RETRYING:
/* Inform registered users about line being disabled.
If we were called in this state, then it is sure
that the previous state was different. */
Line->state = RETRYING;
Line->RetryPeriod = Line->RetryPeriods[Line->RetryIndex];
if (InformUsersCount > 0) {
inform_users_about_line(Index, PreviousState);
}
break;
default:
logger(1, "PROTOCOL, Illegal line state=%d in Restart_chan\n",
Line->state);
break;
}
/* Check whether we are in shutdown process.
If so, and the line has changed state to INACTIVE,
change its state to SIGNEDOFF */
if (MustShutDown < 0)
if ((Line->state == INACTIVE) ||
(Line->state == RETRYING) ||
(Line->state == LISTEN))
Line->state = SIGNOFF;
}
/*
| Some input has been arrived from some line.
*/
void
input_arrived(Index, status, buffer, size)
const int Index, /* Index in IoLines */
status; /* VMS I/O status of read */
int size;
void *buffer;
{
struct LINE *Line;
const unsigned char *q = NULL;
unsigned char *p = NULL;
Line = &(IoLines[Index]);
logger(3, "PROTOCOL: Input from line %s, status=%d, size=%d\n",
Line->HostName, status, size);
/* First check the status. If error, then continue accordingly */
if ((status & 0x1) == 0) {
input_read_error(Index, status, Line);
return;
}
/* No error, something was received - handle it */
p = (void*)buffer;
/* VMnet tends to preceed some buffers with SYN... remove it */
while ((*p == SYN) && (size > 0)) {
++p; --size;
}
#ifdef DEBUG
logger(4, "PROTOCOL: line=%s, Data received:\n", Line->HostName);
trace(p, size, 5);
#endif
/* Now, check the code. Check first for the 3 known blocks
which has a special structure (SOH-ENQ, NAK and ACK) */
q = p + 1; /* q points to the next character */
Line->stats.TotalIn++;
if ((*p == DLE) && (*q == STX)) {
handle_text_block(Index, p, size);
return;
}
if (*p == NAK) {
Line->stats.RetriesIn += 1;
handle_nak(Index);
return;
}
if ((*p == SOH) && (*q == ENQ)) {
handle_enq(Index);
return;
}
if ((*p == DLE) && (*q == ACK0)) {
Line->stats.AckIn += 1;
handle_ack(Index, EXPLICIT_ACK); return;
}
/* If we are here, this block has an invalid format */
logger(1, "PROTOCOL, Illegal block format (line=%s):\n",
Line->HostName);
trace(buffer, size, 1);
error_retry(Index, Line);
}
/*
| The input routine has returned error status. Try recovering if we are in
| ACTIVE state and this is not a repetitive error.
| Called from Input_arrived() when the input routine returns error.
*/
static void
input_read_error(Index, status, Line)
const int Index, status;
struct LINE *Line;
{
logger(1, "PROTOCOL: Read error, on line %s, status=%d\n",
Line->HostName, status);
Line->TotalErrors++; /* Increment total errors */
/* We handle here only timeouts which has one of the two codes: */
if ((status != SS_ABORT) && (status != SS_TIMEOUT))
return; /* Not it, we can't handle it */
switch (Line->state) {
case ACTIVE: /* There is some activity. Try recovery */
error_retry(Index, Line);
break;
case DRAIN: /* try to start line again */
send_data(Index, &Enquire, sizeof(struct ENQUIRE), SEND_AS_IS);
break; /* Continue to send ENQ */
case I_SIGNON_SENT:
case F_SIGNON_SENT:
restart_channel(Index);
break; /* Restart all files */
default:
break; /* Line is not active */
}
}
/*
| Handle received NAK. Check the error count, if it is still small,
| retransmit the last sent buffer.
*/
static void
handle_nak(Index)
const int Index;
{
struct LINE *Line;
Line = &(IoLines[Index]);
Line->TotalErrors++; /* Count in total errors for that line */
/* Check in which state are we */
switch (Line->state) {
case DRAIN:
case I_SIGNON_SENT:
case F_SIGNON_SENT:
logger(1,"PROTOCOL: NAK received on DRAIN/SIGNON time on line %s\n",Line->HostName);
restart_channel(Index);
break;
case ACTIVE:
/* Try re-sending last buffer */
Line->TotalErrors++; /* Count total errors on line */
if ((Line->errors++) < MAX_ERRORS &&
Line->XmitSize != 0) {
logger(2, "PROTOCOL: NAK received on line %s. Re-sending last buffer\n",Line->HostName);
/* Not too many errors, re-send last buffer */
send_data(Index, Line->XmitBuffer, Line->XmitSize, SEND_AS_IS);
} else { /* Too many errors. Restart the line */
logger(1, "PROTOCOL: Too many NAKs on line %s, restarting.\n",
Line->HostName);
restart_channel(Index);
}
break;
default: /* Ignore in other states */
break;
}
/* Other states - simply ignore it */
return;
}
/*
| Handle the SOH-ENQ block. If we are in the starting position, ack it. In
| any other place, restart the channel, since the other party is also
| starting.
*/
static void
handle_enq(Index)
const int Index;
{
struct LINE *Line;
Line = &(IoLines[Index]);
Line->errors = 0; /* Clear error count. */
switch (Line->state) {
case DRAIN: /* Send the ACK block to start Sign-on */
send_data(Index,
&PositiveAck, sizeof(struct POSITIVE_ACK),
SEND_AS_IS);
break;
default:
logger(1,"PROTOCOL: Got SOH-ENQ while not in DRAIN state on line %s\n",Line->HostName);
restart_channel(Index); /* Reset the line */
break;
}
}
/*
| Handle an ACK block. If we are in starting position, its time to send
| the initial signon record. If not, Then it acks something.
| The flag tells us whether this was an ACK block (EXPLICIT_ACK), or whether
| we got a text block which is an implicit ack (IMPLICIT_ACK).
*/
void
handle_ack(Index, flag)
const int Index;
const short flag; /* Is this an implicit or explicit ACK? */
{
register struct LINE *Line;
register int i, CurStream, Switches = 7;
Line = &(IoLines[Index]);
if (Line->socket < 0) return; /* Socket had crashed before
our call happened... */
Line->errors = 0; /* Clear error count. */
/* Should we wait for ACK at all??? */
switch (Line->state) {
case SIGNOFF:
return; /* Keep old state, it is SIGNOFF! */
case INACTIVE:
case I_SIGNON_SENT: /* This is an illegal ACK */
logger(1, "PROTOCOL: Illegal ACK. state=%d, line=%s\n",
Line->state, Line->HostName);
restart_channel(Index);
return;
case DRAIN: /* Is is the ACK for the Enquire - initiate signon */
/* Reset BCB was sent in the first packet */
Line->OutBCB = 0; Line->flags &= ~F_RESET_BCB;
for (i = 0; i < MAX_STREAMS; i++)
Line->InStreamState[i] =
Line->OutStreamState[i] = S_INACTIVE;
/* Line is starting - streams are all idle */
send_signon(Index, INITIAL_SIGNON);
Line->state = I_SIGNON_SENT;
return;
case F_SIGNON_SENT:
Line->state = ACTIVE; /* Line has finished signon */
/* XX: Lets (re)queueing with link-activation time 'debug_rescan_queue()'
routine.. (Gerald Hanush) */
debug_rescan_queue("<signon acked>",'+');
break;
default:
break;
}
Switches = Line->MaxStreams;
SwitchStream:
/* Check whether we are in the Wait-a-bit mode.
If so - only send Acks. Send Ack immediately
if something was received from the other side.
If not, send it after a delay.
WAIT_A_BIT is from the NJE block header.
WAIT_V_A_BIT is from VMnet. */
if (Line->flags & (F_WAIT_A_BIT | F_WAIT_V_A_BIT)) {
if (flag != EXPLICIT_ACK) {
logger(4, "PROTOCOL: Sending ACK, line=%d\n",Index);
SEND_ACK();
return;
}
/* Nothing was received - delay the ACK */
/* Queue it only if we'll have to send it */
if (((Line->flags & F_RELIABLE) == 0) ||
(Line->state != ACTIVE))
queue_timer(1, Index, T_SEND_ACK);
return;
}
/* It is ACK for something - test for what.
Test only outgoing streams, since explicit
ACK can't come when we receive a stream...
Before we check it, check whether there is
an interactive message waiting.
If so, send it and don't handle this ACK
farther as it is impossible to mix NMRs
and other records in the same block.
If we have interactive messages waiting,
then try to block as much as we can in one block. */
if (
#ifdef NBSTREAM /* When blocking, DO NOT write messages to it! */
Line->WritePending == NULL &&
#endif
Line->MessageQstart != NULL) {
/* logger(2,"PROTOCOL: **DEBUG** About to call fill_message_buffer(%d,..): Line %s, MessageEntry = 0x%x\n",
Index,Line->HostName,Line->MessageQstart); */
fill_message_buffer(Index, Line);
return;
}
/* Check whether we have a file waiting for sending,
and free streams to initiate it */
if ((Line->QueuedFilesWaiting > 0) && (Line->FreeStreams > 0) &&
#ifdef NBSTREAM
(Line->WritePending == NULL) &&
#endif
((Line->flags & F_SHUT_PENDING) == 0))
if (request_to_send_file(Index, Line) != 0)
return; /* If 0 - we have to send an ACK */
/* Check whether there is another active stream.
If so, switch to it. */
if (Line->state != ACTIVE)
return; /* If the LINK is not active, quit now.. */
#ifdef NBSTREAM
if (Line->WritePending != NULL) {
/* Line state is ACTIVE */
Line->flags |= F_CALL_ACK;
return; /* There is write pending, quit now... */
}
#endif
if (Line->MaxStreams > 1) {
/* No need to do it if only one stream */
for (i = (Line->CurrentStream + 1) % Line->MaxStreams;
/* Don't jump over maximum streams defined for this line */
i != Line->CurrentStream;
i = (i + 1) % Line->MaxStreams)
if ((Line->ActiveStreams & (1 << i)) != 0)
break; /* Found an active stream */
Line->CurrentStream = i;
}
/* No interactive message - handle the ack */
CurStream = Line->CurrentStream;
switch (Line->OutStreamState[CurStream]) {
/* If there is a file to send, open it and send
a request to initiate a stream. */
case S_REQUEST_SENT: /* It is ack for the request. ACK it back */
SEND_ACK();
return;
case S_INACTIVE:
/* Send another ACK, but after 1 second delay. */
/* Check whether the line has to signoff */
if ((Line->flags & F_SHUT_PENDING) != 0) {
for (i = Line->MaxStreams; i >= 0; --i)
if ((Line->InStreamState[i] != S_INACTIVE) &&
(Line->InStreamState[i] != S_REFUSED))
break;
if (i < 0) {
/* Can shut */
send_data(Index, &SignOff, sizeof(struct SIGN_OFF),
ADD_BCB_CRC);
Line->state = SIGNOFF;
logger(1, "PROTOCOL, Line %s signed off due to operator request\n",
Line->HostName);
inform_users_about_line(Index, -1);
close_line(Index);
Line->state = SIGNOFF;
/* close_line() changes the state
to RETRY, and we don't want it */
can_shut_down();
/* Check whether the daemon need shut */
return;
}
/* Not all streams idle - Ack and don't open a new file */
Line->stats.AckOut++;
SEND_ACK();
return;
}
if (flag != EXPLICIT_ACK) {
/* Ack it right away, since it is either an implicit ACK
(which came with data) so we must ack it immediately,
or because this is a delayed ACK, which we should now
send (the line is idle, so the ack was not sent
immediately the previous time). */
logger(4,"PROTOCOL: Sending ACK - after INACTIVE, line=%s:%d\n",
Line->HostName,Line->CurrentStream);
Line->stats.AckOut++;
SEND_ACK();
return;
}
/* Queue it only if we'll have to send it
on an idle BiSync line */
if (((Line->flags & F_RELIABLE) == 0) ||
(Line->state != ACTIVE))
queue_timer(1, Index, T_SEND_ACK);
return;
case S_REFUSED:
/* If refused - just chitchat. */
if ((Line->flags & F_SHUT_PENDING) != 0) {
/* Can shut ? */
for (i = Line->MaxStreams; i >= 0; --i)
if ((Line->InStreamState[i] != S_INACTIVE) &&
(Line->InStreamState[i] != S_REFUSED))
break;
if (i < 0) {
/* Can shut ! */
send_data(Index, &SignOff, sizeof(SignOff), ADD_BCB_CRC);
Line->state = SIGNOFF;
logger(1, "PROTOCOL, Line %s signed off due to operator request\n",
Line->HostName);
inform_users_about_line(Index, -1);
close_line(Index);
Line->state = SIGNOFF;
/* close_line() changes the state
to RETRY, and we don't want it */
can_shut_down();
/* Check whether the daemon need shut */
return;
}
/* Stream not idle - Ack and don't open a new file */
Line->stats.AckOut++;
SEND_ACK();
if (--Switches < 0) return;
goto SwitchStream;
}
if (flag != EXPLICIT_ACK) {
/* Ack it right away, since it is either an implicit ACK
(which came with data) so we must ack it immediately,
or because this is a delayed ACK, which we should now
send (the line is idle, so the ack was not sent
immediately the previous time). */
logger(4, "PROTOCOL: Sending ACK - after REFUSED, line=%s:d\n",
Line->HostName,Line->CurrentStream);
Line->stats.AckOut++;
SEND_ACK();
return;
}
/* Can it be a link that is shutting down ?
If there is some activity aside of S_REFUSED,
go in there.. (try anyway) */
for (i = Line->MaxStreams; i >= 0; --i)
if ((Line->OutStreamState[i] != S_INACTIVE) &&
(Line->OutStreamState[i] != S_REFUSED))
break;
if (i >= 0) goto SwitchStream;
/* Queue it only if we'll have to send it
on an idle BiSync line */
if (((Line->flags & F_RELIABLE) == 0) &&
(Line->state == ACTIVE))
queue_timer(1, Index, T_SEND_ACK);
return;
case S_NJH_SENT:
logger(3, "PROTOCOL: Sending Dataset header\n");
Line->flags |= F_XMIT_CAN_WAIT; /* We support TCP lines here */
/* Returns 1 if another fragment to send.. */
if (send_njh_dsh_record(Index, SEND_DSH,
Line->OutFileParams[CurStream].type & F_JOB))
Line->OutStreamState[CurStream] = S_NDH_SENT;
else
Line->OutStreamState[CurStream] = S_SENDING_FILE;
/* If it is VMNET protocol, and more room in transmit
buffers, don't return; fall to next transmit block */
Line->flags &= ~F_XMIT_CAN_WAIT;
if ((Line->flags & F_XMIT_MORE) == 0 ||
Line->state != ACTIVE)
return;
case S_NDH_SENT:
/* We fall thru from above.. */
if (Line->OutStreamState[CurStream] == S_NDH_SENT) {
logger(3, "PROTOCOL: Sending Dataset header, fragment 2\n");
Line->flags |= F_XMIT_CAN_WAIT; /* We support TCP lines here */
send_njh_dsh_record(Index, SEND_DSH2,
Line->OutFileParams[CurStream].type & F_JOB);
Line->OutStreamState[CurStream] = S_SENDING_FILE;
/* If it is VMNET protocol, and more room in transmit
buffers, don't return; fall to next transmit block */
Line->flags &= ~F_XMIT_CAN_WAIT;
if ((Line->flags & F_XMIT_MORE) == 0 ||
Line->state != ACTIVE)
return;
}
case S_SENDING_FILE: /* We are in the middle of the file */
SendAgain:
/* logger(3, "PROTOCOL: Sending next file's buffer\n"); */
#ifdef NBSTREAM
if (Line->WritePending != NULL) {
if (Line->state == ACTIVE)
Line->flags |= F_CALL_ACK;
return; /* Brp.. It is full.. */
}
#endif
/* Should we delay a bit ? */
if (Line->flags & (F_WAIT_V_A_BIT | F_WAIT_A_BIT))
return;
Line->flags |= F_XMIT_CAN_WAIT;
send_file_buffer(Index); /* pick next record */
/* If it is VMNET protocol, and more room in transmit
buffers, don't return; fall to next transmit block */
Line->flags &= ~F_XMIT_CAN_WAIT;
if (Line->state != ACTIVE)
return;
if ((Line->flags & F_XMIT_MORE) == 0) {
Line->flags |= F_CALL_ACK;
return;
}
#if 1
/* If a SLOW-INTERLEAVE is asked for the link, do the
dance locally in here, else return and let the upper
stages do it. SLOW-INTERLEAVE might be a bit more
efficient at feeding thru large files on the links.. */
if (Line->OutStreamState[CurStream] == S_SENDING_FILE)
if ((Line->flags & F_SLOW_INTERLEAVE) != 0)
goto SendAgain;
else
goto SwitchStream;
#else
if (Line->OutStreamState[CurStream] == S_SENDING_FILE)
goto SendAgain;
#endif
/* else - fall to send-njt... */
case S_EOF_FOUND: /* Send the NJT block */
/* If we send EBCDIC files and TCP line, we fall here
by mistake. In this case, do not send NJT, since it
was already sent as part of stored file. */
if (Line->OutStreamState[CurStream] != S_NJT_SENT) {
logger(3, "PROTOCOL: Sending NJT on line %s:%d\n",
Line->HostName,CurStream);
Line->OutStreamState[CurStream] = S_NJT_SENT;
Line->flags |= F_XMIT_CAN_WAIT;
send_njt(Index,
Line->OutFileParams[CurStream].type & F_JOB);
Line->flags &= ~F_XMIT_CAN_WAIT;
/* If it is VMNET protocol, and more room in transmit
buffers, don't return; fall to next transmit block */
if ((Line->flags & F_XMIT_MORE) == 0 ||
Line->state != ACTIVE)
return;
}
case S_NJT_SENT:
/* The NJT was sent and ACKED. Send EOF now. */
logger(3,"PROTOCOL: line=%s:%d: NJT sent+ACKed, sending EOF.\n",
Line->HostName,CurStream);
if (Line->OutFileParams[CurStream].type & F_JOB)
EOFblock.RCB = (((CurStream + 9) << 4) | 0x8);
else
EOFblock.RCB = (((CurStream + 9) << 4) | 0x9);
Line->OutStreamState[CurStream] = S_EOF_SENT;
/* Since we do not set the flag F_XMIT_CAN_WAIT,
this will force the TCP layer to send the data,
even if the buffer is not full. */
Line->flags &= ~F_XMIT_CAN_WAIT;
send_data(Index, &EOFblock,
sizeof(struct EOF_BLOCK),
ADD_BCB_CRC);
return;
case S_EOF_SENT: /* We are waiting now for the final
completion block */
logger(3, "PROTOCOL: EOF sent and confirmed by ACK. ACKED back. Line=%s:%d\n",
Line->HostName, CurStream);
(void) dequeue_file_entry_ok(Index,Line,1);
delete_file(Index, F_INPUT_FILE, CurStream);
Line->OutStreamState[CurStream] = S_INACTIVE;
Line->FreeStreams += 1;
/* Clear its bit */
Line->ActiveStreams &= ~(1 << CurStream);
rscsacct_log(&Line->OutFileParams[CurStream],1);
SEND_ACK();
return;
default:
logger(1, "PROTOCOL: Line %s:%d, ACK received when line operation=%d. Illegal.\n",
Line->HostName, CurStream,
Line->OutStreamState[CurStream]);
restart_channel(Index);
return;
}
}
/*
| Called from Handle_ack() when there are NMRs to send to other side. Try
| blocking as much messages as you can and send them.
*/
static void
fill_message_buffer(Index, Line)
const int Index;
struct LINE *Line;
{
struct MESSAGE *MessageEntry; /* Messages' queue for the line */
unsigned char buffer[MAX_BUF_SIZE];
register long size, position, MaxSize;
Line->stats.MessagesOut += 1; /* [mea] Count messages always */
MaxSize = Line->MaxXmitSize - 20; /* The space we have after
counting the overheads */
position = 0;
while (Line->MessageQstart != NULL) {
size = Line->MessageQstart->length;
if ((position + size) > MaxSize) break; /* No room for more */
memcpy(&buffer[position],
(unsigned char *)(Line->MessageQstart->text),
size);
position += size;
/* Dequeue this entry */
MessageEntry = Line->MessageQstart;
/* logger(2,"PROTOCOL: **DEBUG** DEQUEUE THIS MSG: Line %s, MessageEntry = 0x%x, ->next = 0x%x, size=%d\n",
Line->HostName,MessageEntry,MessageEntry->next,
MessageEntry->length); */
if (MessageEntry->next == NULL) { /* End of list */
Line->MessageQstart = NULL;
Line->MessageQend = NULL;
} else
Line->MessageQstart = MessageEntry->next;
free(MessageEntry);
}
/* Send the message */
buffer[position++] = NULL_RCB; /* Final RCB */
send_data(Index, buffer, position, ADD_BCB_CRC);
return;
}
/*
| The outgoing stream is inactive and we have a file to send. Open it, and
| if it is ok, send a request to send to the other side.
| This function is called by Handle_ack().
| Returns 0 if not successfull (then a normal ACK should be sent) or 1 if
| successfull (the request to init a stream will serve as an implicit ack).
*/
static int
request_to_send_file(Index, Line)
const int Index;
struct LINE *Line;
{
struct QUEUE *FileEntry;
unsigned char buffer[4]; /* To send the request block */
int i;
struct FILE_PARAMS *FP;
/* Dequeue the first file entry and
init the various variables of the file */
FileEntry = pick_file_entry(Index,Line);
if (FileEntry == NULL) return 0; /* Uhh.. Some error.. */
/* Find which stream is free */
for (i = 0; i < Line->MaxStreams; i++)
if ((Line->ActiveStreams & (1 << i)) == 0)
break; /* Found an inactive stream */
if (i >= Line->MaxStreams)
bug_check("Aborting because attempted to request_to_send_file() on link w/o free streams! (ActiveStreams flag corruption?)");
Line->CurrentStream = i;
FP = &Line->OutFileParams[Line->CurrentStream];
/* Open the file */
FileEntry->state = 1; /* Mark it for sending */
FP->FileEntry = FileEntry;
strcpy(FP->SpoolFileName,FileEntry->FileName); /* Save spool file name */
if (open_xmit_file(Index, Line->CurrentStream,
FP->SpoolFileName) == 0) {
Line->OutStreamState[Line->CurrentStream] = S_INACTIVE;
/* Some error in file, fall to send ACK */
FileEntry->state = -2; /* Place it into HELD status */
return 0;
} else {
logger(2,"PROTOCOL: queued file `%s' fn.ft: `%s %s' for sending on stream %s:%d\n",
FileEntry->FileName,
FP->FileName,FP->FileExt,
Line->HostName, Line->CurrentStream);
Line->WrFiles += 1;
/* Create a request block and send it */
buffer[0] = REQUEST_RCB; /* RCB */
if (FP->type & F_JOB)
buffer[1] = (((Line->CurrentStream + 9) << 4) + 0x8);
else
buffer[1] = (((Line->CurrentStream + 9) << 4) + 0x9);
buffer[2] = NULL_RCB; /* Null string */
buffer[3] = NULL_RCB; /* End of block */
Line->OutStreamState[Line->CurrentStream] = S_REQUEST_SENT;
/* Made one stream active */
Line->FreeStreams -= 1;
/* Mark the specific stream */
Line->ActiveStreams |= (1 << Line->CurrentStream);
/* When we started */
GETTIME(&FP->XmitStartTime);
/* Decrease the counter */
Line->QueuedFilesWaiting -= 1;
send_data(Index, buffer, 4, ADD_BCB_CRC);
logger(3, "Sent request for transmission.\n");
return 1; /* No need to send ACK */
}
}
/*
| A text block was received. Look what text block it is, and process
| accordingly.
*/
static void
handle_text_block(Index, Buffer, size)
const int Index;
int size;
void *Buffer;
{
int SendAck; /* Shall we treat this message as an
implicit ack? */
struct LINE *Line;
unsigned char BCB, RCB, *pointer, line[MAX_BUF_SIZE];
register int i;
int SizeConsumed; /* Size consumed from input buffer
by uncompress routine */
TIMETYPE dt;
char *dts;
unsigned char *buffer = Buffer;
Line = &IoLines[Index];
/* Check the received CRC.
The procedure that does it also discards double DLE's.
However, don't call this procedure if the line is of
TCP type, since these lines don't send CRC characters. */
if ((Line->flags & F_RELIABLE) == 0) {
if ((Line->type == DMB) || (Line->type == DSV)) {
/* Just remove double DLE's */
remove_dles(buffer, &size);
}
else {
if (check_crc(buffer, &size) == 0) {
/* CRC error */
error_retry(Index, Line);
return;
}
}
}
Line->errors = 0; /* Clear error count. */
/* Check the BCB now. If incorrect - Cry... */
BCB = buffer[BCB_OFFSET];
switch(BCB & 0xf0) {
case 0x80: /* Normal block - check sequence */
if (Line->InBCB == (BCB & 0x0f)) { /* OK */
logger(4, "Received BCB is ok.\n");
Line->InBCB = (Line->InBCB + 1) % 16; /* Increment it */
break;
} else {
/* Check whether this is the preceeding BCB.
If so - discard the message */
if (Line->InBCB == (((BCB & 0x0f) + 1) % 16)) {
logger(2, "PROTOCOL: Line %s, Duplicate block discarded.\n",
Line->HostName);
handle_ack(Index, IMPLICIT_ACK);
return;
}
/* BCB sequence error - probably we missed a block.
Restart the line */
logger(1, "PROTOCOL: Line %s, Incorrect BCB received(%d), expect(%d)\n",
Line->HostName, BCB & 0xf, Line->InBCB);
trace(buffer,BCB_OFFSET+4,1);
restart_channel(Index);
return;
}
case 0x90: /* Bypass BCB count - ignore it and do not increment */
break;
case 0xa0: /* Reset BCB */
logger (2, "PROTOCOL: Line %s, Income BCB reset to %d\n",
Line->HostName, BCB & 0xf);
Line->InBCB = (BCB & 0xf);
break;
default:
logger(1, "PROTOCOL: Line %s, Illegal BCB (%x). Reseting line. Trace:\n",
Line->HostName, BCB);
trace(buffer, size, 1);
restart_channel(Index);
return;
}
/* Check which type of block it is.
Currently ignore the FCS bits.
First check the ones that occupy
a whole block and are not compressed. */
if (buffer[RCB_OFFSET] == SIGNON_RCB) {
/* Signon Control record */
income_signon(Index, Line, buffer);
return;
}
/* Test whether the Wait-a-bit (suspend all streams) is on.
If so - mark it, so the routine that sends the reply will
handle it properly. */