-
Notifications
You must be signed in to change notification settings - Fork 0
/
ckcfn2.c
3350 lines (3093 loc) · 97.8 KB
/
ckcfn2.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
/* C K C F N 2 -- System-independent Kermit protocol support functions... */
/* ...Part 2 (continued from ckcfns.c) */
/*
Author: Frank da Cruz <fdc@columbia.edu>,
Columbia University Academic Information Systems, New York City.
Copyright (C) 1985, 2011,
Trustees of Columbia University in the City of New York.
All rights reserved. See the C-Kermit COPYING.TXT file or the
copyright text in the ckcmai.c module for disclaimer and permissions.
*/
/*
Note -- if you change this file, please amend the version number and date at
the top of ckcfns.c accordingly.
*/
#include "ckcsym.h" /* Compilation options */
#include "ckcdeb.h" /* Debugging and other symbols */
#include "ckcasc.h" /* ASCII symbols */
#include "ckcker.h" /* Kermit symbols */
#include "ckcxla.h" /* Translation */
#include "ckcnet.h" /* IKS and VMS #define TCPSOCKET */
#ifdef TCPSOCKET /* For TELNET business in spack() */
extern int tn_nlm, ttnproto, tn_b_nlm;
#endif /* TCPSOCKET */
extern int parity, network, local, interrupted, fatalio, wasclosed;
int kstartactive = 0; /* Flag for kstart() in a packet */
static CHAR p_tbl[] = { /* Even parity table for dopar(). */
(CHAR) '\000', /* ANSI C casts '\ooo' constants */
(CHAR) '\201', /* to signed char, so we have to */
(CHAR) '\202', /* cast back to unsigned char... */
(CHAR) '\003',
(CHAR) '\204',
(CHAR) '\005',
(CHAR) '\006',
(CHAR) '\207',
(CHAR) '\210',
(CHAR) '\011',
(CHAR) '\012',
(CHAR) '\213',
(CHAR) '\014',
(CHAR) '\215',
(CHAR) '\216',
(CHAR) '\017',
(CHAR) '\220',
(CHAR) '\021',
(CHAR) '\022',
(CHAR) '\223',
(CHAR) '\024',
(CHAR) '\225',
(CHAR) '\226',
(CHAR) '\027',
(CHAR) '\030',
(CHAR) '\231',
(CHAR) '\232',
(CHAR) '\033',
(CHAR) '\234',
(CHAR) '\035',
(CHAR) '\036',
(CHAR) '\237',
(CHAR) '\240',
(CHAR) '\041',
(CHAR) '\042',
(CHAR) '\243',
(CHAR) '\044',
(CHAR) '\245',
(CHAR) '\246',
(CHAR) '\047',
(CHAR) '\050',
(CHAR) '\251',
(CHAR) '\252',
(CHAR) '\053',
(CHAR) '\254',
(CHAR) '\055',
(CHAR) '\056',
(CHAR) '\257',
(CHAR) '\060',
(CHAR) '\261',
(CHAR) '\262',
(CHAR) '\063',
(CHAR) '\264',
(CHAR) '\065',
(CHAR) '\066',
(CHAR) '\267',
(CHAR) '\270',
(CHAR) '\071',
(CHAR) '\072',
(CHAR) '\273',
(CHAR) '\074',
(CHAR) '\275',
(CHAR) '\276',
(CHAR) '\077',
(CHAR) '\300',
(CHAR) '\101',
(CHAR) '\102',
(CHAR) '\303',
(CHAR) '\104',
(CHAR) '\305',
(CHAR) '\306',
(CHAR) '\107',
(CHAR) '\110',
(CHAR) '\311',
(CHAR) '\312',
(CHAR) '\113',
(CHAR) '\314',
(CHAR) '\115',
(CHAR) '\116',
(CHAR) '\317',
(CHAR) '\120',
(CHAR) '\321',
(CHAR) '\322',
(CHAR) '\123',
(CHAR) '\324',
(CHAR) '\125',
(CHAR) '\126',
(CHAR) '\327',
(CHAR) '\330',
(CHAR) '\131',
(CHAR) '\132',
(CHAR) '\333',
(CHAR) '\134',
(CHAR) '\335',
(CHAR) '\336',
(CHAR) '\137',
(CHAR) '\140',
(CHAR) '\341',
(CHAR) '\342',
(CHAR) '\143',
(CHAR) '\344',
(CHAR) '\145',
(CHAR) '\146',
(CHAR) '\347',
(CHAR) '\350',
(CHAR) '\151',
(CHAR) '\152',
(CHAR) '\353',
(CHAR) '\154',
(CHAR) '\355',
(CHAR) '\356',
(CHAR) '\157',
(CHAR) '\360',
(CHAR) '\161',
(CHAR) '\162',
(CHAR) '\363',
(CHAR) '\164',
(CHAR) '\365',
(CHAR) '\366',
(CHAR) '\167',
(CHAR) '\170',
(CHAR) '\371',
(CHAR) '\372',
(CHAR) '\173',
(CHAR) '\374',
(CHAR) '\175',
(CHAR) '\176',
(CHAR) '\377'
};
/* D O P A R -- Add an appropriate parity bit to a character */
CHAR
#ifdef CK_ANSIC
dopar(register CHAR ch)
#else
dopar(ch) register CHAR ch;
#endif /* CK_ANSIC */
{
register unsigned int a;
if (!parity
#ifdef TCPSOCKET
|| (network && (ttnproto == NP_TELNET) && (TELOPT_ME(TELOPT_BINARY)))
#ifndef NOXFER
|| (!local && sstelnet) /* TELNET BINARY MODE */
#endif /* NOXFER */
#endif /* TCPSOCKET */
) return((CHAR) (ch & 255)); else a = ch & 127;
switch (parity) {
case 'e': return(p_tbl[a]); /* Even */
case 'm': return((CHAR) (a | 128)); /* Mark */
case 'o': return((CHAR) (p_tbl[a] ^ 128)); /* Odd */
case 's': return((CHAR) a); /* Space */
default: return((CHAR) a); /* Something illegal */
}
}
#ifndef NOXFER /* Rest of this file... */
#define NEWDPL /* New dynamic packet length method */
#ifdef VMS
extern int batch;
#else
extern int backgrd;
#endif /* VMS */
#ifdef DYNAMIC
extern struct pktinfo *s_pkt; /* array of pktinfo structures */
extern struct pktinfo *r_pkt; /* array of pktinfo structures */
#else
extern struct pktinfo s_pkt[]; /* array of pktinfo structures */
extern struct pktinfo r_pkt[]; /* array of pktinfo structures */
#endif /* DYNAMIC */
extern int sseqtbl[], rseqtbl[], sbufuse[], sacktbl[], wslots, winlo, wslotn,
sbufnum, rbufnum, pktpaus, reliable;
#ifdef STREAMING
static int dontsend = 0;
extern int streaming;
#endif /* STREAMING */
extern int ttprty; /* from ck*tio.c */
extern int autopar;
extern int spsiz, spmax, rpsiz, timint, timef, npad, bestlen, maxsend;
extern int rpt, rptq, rptflg, capas, spsizf, en_fin, tsecs, flow;
extern int pktnum, sndtyp, rcvtyp, bctr, bctu, bctf, bctl, rsn, rln, maxtry;
extern int size, osize, maxsize, spktl, rpktl, nfils, stdouf, fsecs;
extern int turn, turnch, displa, pktlog, seslog, xflg, mypadn;
extern int hcflg, server, cxseen, czseen, discard, slostart;
extern int nakstate, quiet, success, xitsta, what, filestatus;
extern int spackets, rpackets, timeouts, retrans, crunched, urpsiz;
extern int carrier, fdispla, srvidl;
#ifdef GFTIMER
extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
#endif /* GFTIMER */
extern long filcnt, filrej, speed, filcps, tfcps;
extern CK_OFF_T ffc, flci, flco, tlci, tlco, tfc;
extern char *cmarg, filnam[];
extern CHAR padch, mypadc, eol, seol, ctlq, sstate;
extern CHAR *recpkt, *data, myinit[];
extern CHAR *srvptr, stchr, mystch, *rdatap;
extern CHAR padbuf[];
extern CHAR * epktmsg;
extern int epktrcvd, epktsent;
#ifdef OS2 /* AUTODOWNLOAD parameters */
extern int adl_kmode, adl_zmode; /* Match Packet to signal download */
extern char * adl_kstr; /* KERMIT Download String */
extern char * adl_zstr; /* ZMODEM Download String */
#endif /* OS2 */
#ifdef CK_AUTODL
CHAR ksbuf[96] = { NUL, NUL }; /* Autodownload "Kermit Start" buf */
#endif /* CK_AUTODL */
int numerrs = 0; /* Number of packet errors so far */
int rcvtimo = 0; /* Timeout for receiving a packet */
int idletmo = 0; /* Flag for idle timeout */
long filcps = 0L; /* CPS most recent file transferred */
long tfcps = 0L; /* CPS most recent transaction */
long xfsecs = 0L; /* Elapsed time for most recent file */
#ifdef GFTIMER
CKFLOAT fpxfsecs = 0.0; /* Ditto, but floating point */
#endif /* GFTIMER */
#ifdef CK_TIMERS
int rrttbl[64], srttbl[64]; /* Packet timestamp tables */
extern int rttflg;
#define RTT_SCALE 1000
long
rttsamples, /* Round trip time samples */
rttdelay, /* RTT delay */
pktintvl, /* Interpacket arrival time */
rttvariance, /* RTT variance */
rttstddev; /* RTT standard deviation */
#endif /* CK_TIMERS */
/* CRC generation tables */
long crcta[16] = { 0L, 010201L, 020402L, 030603L, 041004L,
051205L, 061406L, 071607L, 0102010L, 0112211L, 0122412L, 0132613L, 0143014L,
0153215L, 0163416L, 0173617L
};
long crctb[16] = { 0L, 010611L, 021422L, 031233L, 043044L,
053655L, 062466L, 072277L, 0106110L, 0116701L, 0127532L, 0137323L, 0145154L,
0155745L, 0164576L, 0174367L
};
#ifdef CK_TIMERS
/*
Round-trip timer calculations adapted from Tim Kientzle's article,
"Improving Kermit Performance", Dr Dobb's Journal, February 1996.
*/
/* R T T I N I T -- Initialize timers at start of transaction */
VOID
rttinit() { /* Initialize round-trip timing */
int i;
if (timint == 0)
return;
rttsamples = 0L; /* Samples (packets) */
rttvariance = 0L; /* Variance in delay */
rttdelay = (long) timint * RTT_SCALE; /* Delay */
pktintvl = (long) timint * RTT_SCALE; /* Delay */
rttstddev = (long) timint * RTT_SCALE; /* Standard deviation of delay */
/* Tables of timestamps indexed by packet sequence number */
for (i = 0; i < 64; i++) {
rrttbl[i] = -1; /* Time each packet was received */
srttbl[i] = -1; /* Time each packet was sent */
}
rcvtimo = timint; /* Initial timeout is what user said */
}
/* G E T R T T -- Get packet round trip time */
/*
Call with nakstate == 0 if file sender, nonzero if receiver,
and n == packet sequence number of the packet we just received.
Returns:
-1 on failure with rcvtimo set to timint (what the user said), or:
0 on success with rcvtimo set to dynamically calculated value:
1 <= rcvtimo <= timint * 3.
*/
int
getrtt(nakstate, n) int nakstate, n; {
extern int mintime, maxtime;
static int prevz = 0, prevr = 0;
int x, y, yy, z = 0, zz = 0; /* How long did it take to get here? */
rcvtimo = timint; /* Default timeout is what user said */
if (timint == 0) /* We're not timing out. */
return(0);
if (!rttflg) /* Not supposed to be doing this? */
return(-1); /* So don't */
if (!RTT_SCALE) /* Paranoia... */
return(-1);
/* rtimer() (reset timer) is not called until 1st data packet */
#ifdef GFTIMER
/* rftimer(); */
#endif /* GFTIMER */
/* S (F [ A ] D* Z)* B */
/* NOTE: we calculate both the round-trip time AND the packet */
/* arrival rate. We don't use the RTT for anything, we just display it. */
/* Timeouts are based on the packet arrival rate. */
if (spackets > 3) { /* Don't start till 4th packet */
if (nakstate) { /* File receiver */
x = rrttbl[n]; /* Time when I got packet n */
y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
yy = srttbl[n > 0 ? n - 1 : 63]; /* Time when I sent ACK(n-1) */
if (x > -1 && y > -1) { /* Be careful */
z = x - y; /* Packet rate */
zz = x - yy; /* Round trip time */
z++; /* So sender & receiver differ */
debug(F101,"RTT RECV","",z);
} else { /* This shouldn't happen */
debug(F101,"RTT RECV ERROR spackets","",spackets);
debug(F101,"RTT RECV ERROR sequence","",n);
return(-1);
}
} else { /* File sender */
x = rrttbl[n]; /* Time when I got ACK(n) */
y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
yy = srttbl[n]; /* Time when I sent n */
if (x > -1 && y > -1) {
z = x - y; /* Packet rate */
zz = x - yy; /* Round trip time */
debug(F101,"RTT SEND","",z);
} else {
debug(F100,"RTT SEND ERROR","",0);
return(-1);
}
}
if (z < 1) /* For fast connections */
z = RTT_SCALE / 2; /* Convert to scale... */
else
z *= RTT_SCALE;
debug(F101,"RTT z scaled","",z);
if (zz < 1) /* For fast connections */
zz = RTT_SCALE / 2; /* Convert to scale... */
else
zz *= RTT_SCALE;
rttdelay = zz; /* Round trip time of this packet */
#ifdef COMMENT
/*
This was used in C-Kermit 7.0 (and 6.0?) but not only is it overkill,
it also can produce ridiculously long timeouts under certain conditions.
Replaced in 8.0 by a far simpler and more aggressive strategy.
*/
if (rttsamples++ == 0L) { /* First sample */
pktintvl = z;
} else { /* Subsequent samples */
long oldavg = pktintvl;
long rttdiffsq;
if (rttsamples > 30) /* Use real average for first 30 */
rttsamples = 30; /* then decaying average. */
/* Average delay, difference squared, variance, std deviation */
pktintvl += (z - pktintvl) / rttsamples;
rttdiffsq = (z - oldavg) * (z - oldavg);
rttvariance += (rttdiffsq - rttvariance) / rttsamples;
debug(F101,"RTT stddev1","",rttstddev);
if (rttstddev < 1L) /* It can be zero, in which case */
rttstddev = RTT_SCALE / 3; /* set it to something small... */
rttstddev = (rttstddev + rttvariance / rttstddev) / 2;
}
debug(F101,"RTT stddev2","",rttstddev);
debug(F101,"RTT delay ","",pktintvl);
rcvtimo = (pktintvl + (3L * rttstddev)) / RTT_SCALE + 1;
if (rpackets < 32) /* Allow for slow start */
rcvtimo += rcvtimo + 2;
else if (rpackets < 64)
rcvtimo += rcvtimo / 2 + 1;
/* On a reliable link, don't try too hard to time out. */
/* Especially on fast local network connections. */
if (server && what == W_NOTHING) /* Server command wait */
rcvtimo = rcvtimo; /* == srvtim */
else if (reliable == SET_ON && rcvtimo > 0) /* Reliable */
rcvtimo = rcvtimo +15; /* and not server command wait */
else /* Not reliable or server cmd wait */
rcvtimo = rcvtimo;
if (rcvtimo < mintime) /* Lower bound */
rcvtimo = mintime;
if (maxtime > 0) { /* User specified an upper bound */
if (rcvtimo > maxtime)
rcvtimo = maxtime;
} else if (maxtime == 0) { /* User didn't specify */
if (rcvtimo > timint * 6)
rcvtimo = timint * 6;
}
#else /* COMMENT */
#ifdef CKFLOAT
{
CKFLOAT x;
x = (CKFLOAT)(prevz + z + z) / 3.0;
rcvtimo = (int)((((CKFLOAT)x * 2.66) / RTT_SCALE) + 0.5);
debug(F101,"RTT rcvtimo (float)","",rcvtimo);
}
#else
rcvtimo = (prevz + z + z) / RTT_SCALE;
debug(F101,"RTT rcvtimo (int)","",rcvtimo);
#endif /* CKFLOAT */
#endif /* COMMENT */
zz = (rttdelay + 500) / 1000;
if (rcvtimo > (zz * 3))
rcvtimo = zz * 3;
if (rcvtimo < 1)
rcvtimo = 1;
if (mintime > 0) {
if (rcvtimo < mintime) /* Lower bound */
rcvtimo = mintime;
}
if (maxtime > 0) { /* Upper bound */
if (rcvtimo > maxtime)
rcvtimo = maxtime;
}
if (rcvtimo == (prevr - 1))
rcvtimo++;
debug(F101,"RTT final rcvtimo","",rcvtimo);
}
prevz = z;
prevr = rcvtimo;
return(0);
}
#endif /* CK_TIMERS */
/* I N P U T -- Attempt to read packet number 'pktnum'. */
/*
This is the function that feeds input to Kermit's finite state machine,
in the form of a character in the range 32-126, normally a packet type
(uppercase letter) or pseudo-packet-type (lowercase letter).
If a special start state is in effect, that state is returned as if it were
the type of an incoming packet.
*/
int
input() {
int type = 0, acktype; /* Received packet type */
int x, y, k; /* Workers */
int z, pi, nf; /* Worker, packet index, NAK flag */
int nak2ack = 0;
debug(F000,"input sstate","",sstate);
debug(F101,"input nakstate","",nakstate);
debug(F000,"input sndtyp","",sndtyp);
debug(F101,"input xitsta","",xitsta);
debug(F101,"input what","",what);
while (1) { /* Big loop... */
/*
It is ttchk()'s responsibility to tell us if the connection is broken,
and to do so instantly and nondestructively -- no blocking, etc, that would
slow down file transfer.
*/
if (ttchk() < 0) {
debug(F100,"input CONNECTION BROKEN","",0);
fatalio = 1;
return('q');
}
if (sstate != 0) { /* If a start state is in effect, */
type = sstate; /* return it like a packet type, */
sstate = 0; /* and then nullify it. */
numerrs = 0; /* (PWP) no errors so far */
return(type);
}
if (nakstate) { /* This section for file receiver. */
if (wslots > 1) { /* If we're doing windows, */
x = rseqtbl[winlo]; /* see if desired packet already in. */
debug(F101,"input winlo","",winlo);
debug(F101,"input rseqtbl[winlo]","",rseqtbl[winlo]);
if (x > -1) { /* Already there? */
if (r_pkt[x].pk_seq == winlo) { /* (double check) */
rsn = winlo; /* Yes, return its info */
debug(F101,"input return pre-stashed packet","",rsn);
dumprbuf();
rdatap = r_pkt[x].pk_adr; /* like rpack would do. */
rln = (int)strlen((char *) rdatap);
type = r_pkt[x].pk_typ;
break;
}
}
}
type = rpack(); /* Try to read a packet. */
debug(F101,"input rpack","",type);
while (type == 'e') { /* Handle echoes */
debug(F101,"input echo discarded","",type);
type = rpack();
}
#ifdef DEBUG
if (deblog) {
if (type == 'D')
debug(F011,"input type D=",(char *)rdatap,39);
else
debug(F000,"input type",(char *)rdatap,type);
}
#endif /* DEBUG */
#ifndef OLDCHKINT
if (type == 'z') {
epktrcvd = 1;
errpkt((CHAR *)"User cancelled.");
type = 'E';
break;
}
#endif /* OLDCHKINT */
if (type < -1) {
char * s;
s = (type == -2) ?
"FAILED - Interrupted" :
"FAILED - Connection lost";
xxscreen(SCR_PT,'q',0L,s);
dologend();
return('q'); /* Ctrl-C or connection lost */
}
if (type < 0) { /* Receive window full */
/* Another thing to do here would be to delete */
/* the highest packet and NAK winlo. But that */
/* shouldn't be necessary since the other Kermit */
/* should not have sent a packet outside the window. */
#ifdef COMMENT
char foo[256];
ckmakxmsg(foo,256,"Receive window full (rpack): wslots=",
ckitoa(wslots)," winlo=",ckitoa(winlo)," pktnum=",
ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
errpkt((CHAR *)foo);
debug(F100,foo,"",0);
#else
errpkt((CHAR *)"Receive window full");
debug(F101,"rpack receive window full","",0);
debug(F101," wslots","",wslots);
debug(F101," winlo","",winlo);
debug(F101," pktnum","",pktnum);
#endif
dumprbuf();
type = 'E';
break;
}
dumprbuf();
#ifdef OLDCHKINT
if (chkint() < 0) { /* Check for console interrupts. */
errpkt((CHAR *)"User cancelled."); /* (old way) */
type = 'E';
break;
}
#endif /* OLDCHKINT */
#ifdef STREAMING
if (streaming) { /* Streaming */
if (type == 'Q' || type == 'T') { /* Errors are fatal. */
crunched++; /* For statistics */
errpkt((CHAR *)"Transmission error on reliable link.");
type = 'E';
}
}
#endif /* STREAMING */
if (type == 'E') {
debug(F101,"input got E, nakstate","",nakstate);
break; /* Error packet */
}
if (type == 'Q') { /* Crunched packet. */
crunched++;
numerrs++;
/*
Packet arrived damaged. It was most likely the packet we were expecting
next, so we send a NAK for that packet. Prior to 5A(189), we always
NAK'd winlo here, but that was bad because if two (or more) different
packets were damaged, we would keep NAKing the first one and never NAK the
other ones, which could result in a lengthy series of timeouts. Now we
NAK the oldest as-yet-unNAK'd missing packet.
*/
#ifdef CK_TIMERS
rcvtimo++; /* Stretch the timeout a little */
#endif /* CK_TIMERS */
z = (winlo + wslots) % 64; /* Search from winlo to z */
debug(F101,"ZZZ crunched z","",z);
nf = 0; /* NAK flag not set yet */
for (x = winlo; x != z; x = (x + 1) % 64) {
debug(F101,"ZZZ x","",x);
if (rseqtbl[x] > -1) /* Have I received packet x? */
continue; /* Yes, go on. */
debug(F101,"ZZZ x not recd yet","",x);
pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
debug(F101,"ZZZ x not NAK'd yet","",x);
nack(x); /* No, NAK it now. */
nf = 1; /* Flag that I did. */
break;
}
}
if (!nf) { /* If we didn't NAK anything above, */
debug(F101,"ZZZ NAKing winlo","",winlo);
if (nack(winlo) < 0) { /* we have to NAK winlo (again) */
errpkt((CHAR *)"Too many retries."); /* Too many */
type = 'E';
break;
}
}
continue;
}
if (type == 'T') { /* Timeout */
#ifndef OS2
/* K95 does this its own way */
if (server && srvidl) {
idletmo = 1;
debug(F101,"SERVER IDLE TIMEOUT","",srvidl);
return('q');
}
#endif /* OS2 */
#ifdef CK_TIMERS
rcvtimo++; /* Stretch the timeout a little */
#endif /* CK_TIMERS */
timeouts++;
debug(F101,"input receive-state timeout, winlo","",winlo);
/* NAK only the packet at window-low */
debug(F101,"input sending NAK for winlo","",winlo);
x = ttchk();
if (x > 0) /* Don't give up if there is still */
continue; /* something to read. */
else if (x < 0) {
dologend();
fatalio = 1;
return('q'); /* Connection Lost */
}
if (nack(winlo) < 0) {
debug(F101,"input sent too many naks","",winlo);
errpkt((CHAR *)"Too many retries.");
type = 'E';
break;
} else continue;
}
if (rsn == winlo) { /* Got the packet we want, done. */
#ifdef CK_TIMERS
if (rttflg && timint) /* Dynamic round trip timers? */
getrtt(nakstate, rsn); /* yes, do it. */
else /* JHD 20100208 */
rcvtimo = timint; /* JHD 20100208 */
#endif /* CK_TIMERS */
debug(F101,"input rsn=winlo","",rsn);
break;
}
/* Got a packet out of order. */
debug(F101,"input out of sequence, rsn","",rsn);
k = rseqtbl[rsn]; /* Get window slot of this packet. */
debug(F101,"input rseqtbl[rsn]","",k);
if (k < 0) {
debug(F101,"input recv can't find index for rcvd pkt","",rsn);
/* Was "Internal error 21" */
/* This should not happen */
errpkt((CHAR *)"Sliding windows protocol error.");
type = 'E';
break;
}
y = chkwin(rsn,winlo,wslots); /* See what window it's in. */
debug(F101,"input recv chkwin","",y);
if (y == 1) { /* From previous window. */
#ifdef STREAMING
if (!streaming) /* NO RESEND IF STREAMING! */
#endif /* STREAMING */
resend(rsn); /* Resend the ACK (might have data) */
freerpkt(rsn); /* Get rid of received packet */
continue; /* Back to wait for another packet */
} else { /* In this window or out of range */
if (y < 0) /* If out of range entirely, */
freerpkt(rsn); /* release its buffer */
#ifdef STREAMING
if (streaming) { /* Streaming (this shouldn't happen) */
errpkt((CHAR *)"Sequence error on reliable link.");
type = 'E';
break;
}
#endif /* STREAMING */
/* If our receive window is full, NAK window-low */
if (rbufnum < 1) { /* Receive window full? */
if (nack(winlo) < 0) { /* No choice, must NAK winlo. */
errpkt((CHAR *)"Too many retries."); /* Too many */
type = 'E';
break;
} else continue;
}
/*
Receive window not full. This is a packet in the current window but it is
not the desired packet at winlo. So therefore there are gaps before this
packet. So we find the "lowest" unNAK'd missing packet, if any, between
winlo and this one, and NAK it. If there are no as-yet-unNAK'd missing
packets in the window, then we send nothing and go wait for another packet.
In theory, this could result in a timeout, but in practice it is likely that
the already-NAK'd missing packets are already on their way. Note, we do not
NAK ahead of ourselves, as that only creates unnecessary retransmissions.
*/
for (x = winlo; x != rsn; x = (x + 1) % 64) {
if (rseqtbl[x] > -1) /* Have I received packet x? */
continue; /* Yes, check next sequence number. */
pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
nack(x); /* No, NAK it now. */
break;
}
}
}
/*!!!*/
} else { /* Otherwise file sender... */
#ifdef STREAMING
if (streaming && sndtyp == 'D') {
debug(F101,"STREAMING input streaming","",streaming);
debug(F000,"STREAMING input sndtyp","",sndtyp);
rsn = winlo;
type = 'Y'; /* Pretend we got an ACK */
}
#endif /* STREAMING */
if (!nak2ack) { /* NAK(n+1) = ACK(n) */
if (wslots > 1) { /* Packet at winlo already ACK'd? */
if (sacktbl[winlo]) { /* If so, */
sacktbl[winlo] = 0; /* Turn off the ACK'd flag */
winlo = (winlo + 1) % 64; /* Rotate the window */
type = 'Y'; /* And return ACK */
debug(F101,
"input send returning pre-stashed ACK","",
winlo-1);
break;
}
}
#ifdef STREAMING
if (!(streaming && sndtyp == 'D')) { /* Not streaming | data */
type = rpack(); /* Try to read an acknowledgement */
} else { /* Streaming and in Data phase */
type = 'Y'; /* Assume all is normal */
if (chkint() < 0) /* Check for console interrupts. */
type = 'z';
else if (ttchk() > 4 + bctu) /* Check for return traffic */
type = rpack();
debug(F000,"input streaming type","",type);
}
#endif /* STREAMING */
debug(F111,"input send",(char *) rdatap,(int) type);
while (type == 'e') { /* Handle echoes */
debug(F000,"echo discarded","",type);
type = rpack();
}
#ifndef OLDCHKINT
if (type == 'z') {
epktrcvd = 1;
errpkt((CHAR *)"User cancelled.");
type = 'E';
break;
}
#endif /* OLDCHKINT */
if (type < -1) {
xxscreen(SCR_PT,'q',0L,
((char *)((type == -2) ?
"Interrupted" :
"Connection lost"))
);
if (type != -2)
dologend();
return('q'); /* Ctrl-C or connection lost */
}
if (type == -1) {
#ifdef COMMENT
char foo[256];
ckmakxmsg(foo,256,
"Receive window full (error 18): wslots=",
ckitoa(wslots),
" winlo=",ckitoa(winlo)," pktnum=",
ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
errpkt((CHAR *)foo);
debug(F100,foo,"",0);
#else
errpkt((CHAR *)"Receive window full"); /* was "internal */
debug(F101," wslots","",wslots); /* error 18" */
debug(F101," winlo","",winlo);
debug(F101," pktnum","",pktnum);
#endif /* COMMENT */
dumprbuf();
type = 'E';
break;
}
dumprbuf(); /* Debugging */
#ifdef OLDCHKINT
if (chkint() < 0) { /* Check for console interrupts. */
errpkt((CHAR *)"User cancelled.");
return(type = 'E');
}
#endif /* OLDCHKINT */
/* Got a packet */
#ifdef STREAMING
if (streaming) { /* Streaming */
if (type == 'Q' || type == 'T') { /* Errors are fatal. */
crunched++; /* For statistics */
errpkt((CHAR *)"Transmission error on reliable link.");
type = 'E';
}
}
#endif /* STREAMING */
if (type == 'E') {
debug(F101,"input send got E, nakstate","",nakstate);
break; /* Error packet */
}
if (type == 'Q') { /* Crunched packet */
crunched++; /* For statistics */
numerrs++; /* For packet resizing */
x = resend(winlo); /* Resend window-low */
if (x < 0) {
type = 'E';
errpkt((CHAR *)"Too many retries");
break;
}
continue;
}
if (type == 'T') { /* Timeout waiting for ACKs. */
timeouts++; /* Count it */
numerrs++; /* Count an error too */
debug(F101,"input send state timeout, winlo","",winlo);
/* Retransmit the oldest un-ACK'd packet. */
debug(F101,"input send resending winlo","",winlo);
if (resend(winlo) < 0) { /* Check retries */
debug(F101,"input send too many resends","",maxtry);
errpkt((CHAR *)"Too many retries");
return(type = 'E');
}
#ifdef NEWDPL
/* Reduce prevailing packet length */
x = sseqtbl[winlo]; /* Get length of packet we want ACKd */
if (x > -1) { /* Only if we have a valid index */
if (s_pkt[x].pk_typ == 'D') { /* only for D packets */
spsiz = (s_pkt[x].pk_len + 8) >> 1; /* halve it */
if (spsiz < 20) spsiz = 20; /* within reason */
debug(F101,"input T cut packet length","",spsiz);
}
}
#endif /* NEWDPL */
continue;
}
}
/* Got an actual normal packet */
nak2ack = 0; /* Unset this flag. */
y = chkwin(rsn,winlo,wslots); /* Is it in the window? */
debug(F101,"input send rsn","",rsn);
debug(F101,"input send winlo","",winlo);
debug(F101,"input send chkwin","",y);
if (type == 'Y') { /* Got an ACK */
if (y == 0) { /* In current window */
if (spackets < 4) /* Error counter doesn't count */
numerrs = 0; /* until data phase. */
sacktbl[rsn]++; /* Mark the packet as ACK'd */
x = sseqtbl[rsn]; /* Get ACK'd packet's buffer index */
debug(F101,"bestlen ack x","",x);
#ifdef NEWDPL
if (x > -1) {
acktype = s_pkt[x].pk_typ; /* Get type */
debug(F000,"bestlen ack type","",acktype);
if (acktype == 'D') { /* Adjust data packet length */
if (spsiz > bestlen) {
bestlen = spsiz;
debug(F101,"bestlen B","",bestlen);
}
#ifdef DEBUG
if (deblog) {
debug(F101,"bestlen retry","",s_pkt[x].pk_rtr);
debug(F101,"bestlen len","",s_pkt[x].pk_len);
debug(F101,"bestlen spackets","",spackets);
}
#endif /* DEBUG */
/* Set new best length */
if (s_pkt[x].pk_rtr == 0 &&
s_pkt[x].pk_len + 8 > bestlen) {
bestlen = s_pkt[x].pk_len + 8;
if (bestlen > spmax)
bestlen = spmax;
debug(F101,"bestlen A","",bestlen);
}
#ifdef DEBUG
if (deblog) {
debug(F101,"bestlen wslots","",wslots);
debug(F101,"bestlen maxsend","",maxsend);
}
#endif /* DEBUG */
/* Slow start */
if (slostart &&
(maxsend <= spmax) &&
(rpackets < 11) &&
(numerrs == 0)) {
spsiz = spsiz << 1;
debug(F101,"bestlen spsiz A","",spsiz);
/* Creep up to best length */
} else if ((spackets > 5) &&
(spsiz < bestlen - 8)) {
spsiz += (bestlen - spsiz) / 3;
debug(F101,"bestlen spsiz B","",spsiz);
/* Push the envelope */
} else if ((spackets % (wslots + 1) == 0) &&
(spackets > 6) &&
(bestlen < spmax - 8) &&
(spsiz < spmax)) {
spsiz += (spmax - bestlen) / 3;
debug(F101,"bestlen spsiz C","",spsiz);
}
/* But not too far */
if (spsiz > spmax) {
spsiz = spmax;
debug(F101,"bestlen spsiz D","",spsiz);
}
}
}
#endif /* NEWDPL */
#ifdef CK_TIMERS
if (rttflg && timint) /* If doing dynamic timers */
getrtt(nakstate, rsn); /* call routine to set it. */
else /* JHD 20100208 */
rcvtimo = timint; /* JHD 20100208 */
#endif /* CK_TIMERS */
/*
NOTE: The following statement frees the buffer of the ACK we just got.
But the upper layers still need the data, like if it's the ACK to an I,
S, F, D, Z, or just about any kind of packet. So for now, freerbuf()
deallocates the buffer, but does not erase the data or destroy the pointer
to it. There's no other single place where these receive buffers can be
correctly freed (?) ...
*/
freerpkt(rsn); /* Free the ACK's buffer */