home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
ek
/
eksw
/
src
/
kermit.c
< prev
next >
Wrap
C/C++ Source or Header
|
2020-01-01
|
102KB
|
3,426 lines
// todo:
// 1. should we send an I packet before the R-00 packet?
// 2. commands to exit the server and/or logout from the session
#define KERMIT_C
/*
* EKSW: Embedded Kermit with true sliding windows
* protocol module Version: 0.94
* Most Recent Update: March 30, 2010
* John Dunlap
*
* Author: Frank da Cruz. Copyright (C) 1995, 2004, Trustees of Columbia
* University in the City of New York. All rights reserved.
*
* No stdio or other runtime library calls, no system calls, no system
* includes, no static data, and no global variables in this module.
*
* Warning: you cannot use debug() in any routine whose argument list does
* not include "struct k_data *k". Thus most routines in this module
* include this arg, even if they don't use it.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of Columbia University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
# if DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
# ifndef __linux
# include "../include/snprintf.h"
# endif
# endif
#include "cdefs.h" /* C language defs for all modules */
#include "debug.h" /* Debugging */
#include "kermit.h" /* Kermit protocol definitions */
#define USE_ZGETC_MACRO
#ifdef USE_ZGETC_MACRO
#define zgetc() \
((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):(*(k->readf))(k)
#else // USE_ZGETC_MACRO
STATIC int
zgetc (struct k_data *k)
{
UCHAR *ptr;
int kar;
k->zincnt--;
if (k->zincnt >= 0)
{
ptr = k->zinptr;
k->zinptr++;
kar = (*ptr) & 0xFF;
}
else
{
kar = (*(k->readf)) (k);
}
return (kar);
}
#endif // USE_ZGETC_MACRO
/*
* See cdefs.h for meaning of STATIC, ULONG, and UCHAR
*/
STATIC ULONG stringnum (UCHAR *, struct k_data *);
STATIC UCHAR *numstring (ULONG, UCHAR *, int, struct k_data *);
STATIC int spkt (char, short, int, UCHAR *, struct k_data *);
STATIC int ack (struct k_data *, short, UCHAR * text);
STATIC int nak (struct k_data *, short, short);
STATIC int chk1 (UCHAR *, struct k_data *);
STATIC USHORT chk2 (UCHAR *, struct k_data *);
STATIC USHORT chk3 (UCHAR *, struct k_data *);
STATIC void spar (struct k_data *, UCHAR *, int);
STATIC int rpar (struct k_data *, char);
STATIC int decode (struct k_data *, struct k_response *, short, UCHAR *,
int rslot);
STATIC int gattr (struct k_data *, UCHAR *, struct k_response *);
STATIC int sattr (struct k_data *, struct k_response *);
STATIC int sdata (struct k_data *, struct k_response *);
STATIC void epkt (char *, struct k_data *);
STATIC int getpkt (struct k_data *, struct k_response *);
STATIC int encstr (UCHAR *, struct k_data *, struct k_response *);
STATIC void encode (int, int, struct k_data *);
STATIC short nxtpkt (struct k_data *);
STATIC int resend (struct k_data *, short seq);
STATIC int nused_sslots (struct k_data *);
STATIC int nused_rslots (struct k_data *);
#if 0
STATIC void decstr (UCHAR *, struct k_data *, struct k_response *);
STATIC short prevpkt (struct k_data *);
#endif
STATIC short earliest_sseq (struct k_data *k);
#ifdef DEBUG
int xerror (void);
STATIC void show_sslots (struct k_data *);
STATIC void show_rslots (struct k_data *);
#endif /* DEBUG */
STATIC int test_rslots (struct k_data *);
STATIC short
earliest_rseq (struct k_data *k)
{
short slot;
short seq;
short seq_ref = -1;
short seq_rot;
short seq_rot_oldest = -1;
short seq_oldest = -1;
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->ipktinfo[slot].seq;
if (seq >= 0 && seq < 64)
{
seq_ref = seq;
seq_oldest = seq;
seq_rot_oldest = 0;
}
}
if (seq_ref != -1)
{
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->ipktinfo[slot].seq;
if (seq >= 0 && seq < 64)
{
seq_rot = seq - seq_ref;
if (seq_rot < -31)
seq_rot += 64;
if (seq_rot > 31)
seq_rot -= 64;
if (seq_rot < seq_rot_oldest)
{
seq_rot_oldest = seq_rot;
seq_oldest = seq;
}
}
}
}
debug (DB_LOG, "EARLIEST_RSEQ", 0, seq_oldest);
return (seq_oldest);
}
STATIC short
latest_rseq (struct k_data *k)
{
short slot;
short seq;
short seq_ref = -1;
short seq_rot;
short seq_rot_newest = -1;
short seq_newest = -1;
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->ipktinfo[slot].seq;
if (seq >= 0 && seq < 64)
{
seq_ref = seq;
seq_newest = seq;
seq_rot_newest = 0;
}
}
if (seq_ref != -1)
{
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->ipktinfo[slot].seq;
if (seq >= 0 && seq < 64)
{
seq_rot = seq - seq_ref;
if (seq_rot < -31)
seq_rot += 64;
if (seq_rot > 31)
seq_rot -= 64;
if (seq_rot > seq_rot_newest)
{
seq_rot_newest = seq_rot;
seq_newest = seq;
}
}
}
}
debug (DB_LOG, "LATEST_RSEQ", 0, seq_newest);
return (seq_newest);
}
// nak_oldest_unacked() never finds anything because
// we ACK as soon as we put each packet into its slot.
// Also, rseq for short packets is unvetted and for long packets
// the only test is a simple checksum so it's safer
// to time out. For Iridium, there are no bad bytes so
// the point is moot.
#undef USE_NAK_OLDEST_UNACKED
#ifdef USE_NAK_OLDEST_UNACKED
STATIC int
nak_oldest_unacked (struct k_data *k, short rseq)
{
short slot;
short seq;
short age;
short oldest_seq = -1;
short oldest_age = -99;
short oldest_rslot = -1;
int flg;
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->ipktinfo[slot].seq;
flg = k->ipktinfo[slot].flg;
if (seq >= 0 && seq < 64 && flg == 0)
{
age = k->s_seq - seq;
if (age < 0)
age += 64;
if (age > 63)
age -= 64;
if (age > oldest_age)
{
oldest_rslot = slot;
oldest_age = age;
oldest_seq = seq;
}
}
}
debug (DB_LOG, "NAK_OLDEST_UNACKED oldest_seq", 0, oldest_seq);
debug (DB_LOG, "NAK_OLDEST_UNACKED rseq", 0, rseq);
if (oldest_seq != -1)
{
nak (k, oldest_seq, oldest_rslot);
// k->anseq = oldest_seq;
return (X_OK);
}
else
{
// nak (k, (k->anseq+1)&63, -1);
// nak (k, k->anseq, -1);
// nak (k, rseq, -1);
return (X_OK);
}
}
#endif /* USE_NAK_OLDEST_UNACKED */
STATIC int
handle_good_rpkt (struct k_data *k, struct k_response *r,
short rseq, UCHAR * pbuf)
{
// true sliding windows for receive see p. 292
short wsize = k->wslots; // negotiated window size
short high = -1; // latest table entry (chronlogically)
short low = -1; // earliest possible table entry
short eseq = -1; // expected sequence number
// short psn = rseq; // just-arrived packet sequence number
short rslot;
short nused;
int isnxt = 0;
int isgap = 0;
int isold = 0;
int rc;
int i;
short iseq;
short dseq;
short nnak;
int do_add_pkt;
nused = nused_rslots (k);
if (nused == 0)
{
eseq = rseq; // for first time
}
else
{
high = latest_rseq (k);
low = high - wsize + 1;
if (low < 0)
low += 64;
eseq = (high + 1) & 63;
if (rseq != eseq)
{
for (i = 2; i <= wsize; i++)
{
iseq = (high + i) & 63;
if (rseq == iseq)
{
isgap = 1;
break;
}
}
}
if (rseq != eseq && !isgap)
{
short hp = (high + 1) & 63;
for (i = low; i != hp; i++)
{
iseq = i & 63;
if (rseq == iseq)
{
isold = 1;
break;
}
}
}
}
if (rseq == eseq)
isnxt = 1;
else
isnxt = 0;
debug (DB_LOG, "HANDLE_RPKT rseq", 0, rseq);
debug (DB_LOG, " high", 0, high);
debug (DB_LOG, " low", 0, low);
debug (DB_LOG, " eseq", 0, eseq);
debug (DB_LOG, " nused", 0, nused);
debug (DB_LOG, " isnxt", 0, isnxt);
debug (DB_LOG, " isgap", 0, isgap);
debug (DB_LOG, " isold", 0, isold);
#ifdef DEBUG
show_rslots (k);
#endif
// write old packets to file in order to keep
// our receive window aligned with sender's window
if (isnxt || isgap)
{
debug (DB_LOG, "HANDLE_RPKT align our recv window with sender rseq",
0, rseq);
for (i = 0; i < wsize; i++)
{
iseq = earliest_rseq (k);
debug (DB_LOG, "HANDLE_RPKT iseq", 0, iseq);
if (iseq < 0 || iseq >= 64)
break;
dseq = rseq - iseq;
if (dseq < 0)
dseq += 64;
debug (DB_LOG, "HANDLE_RPKT dseq", 0, dseq);
if (dseq < wsize)
continue;
rslot = k->r_pw[iseq];
debug (DB_LOG, "HANDLE_RPKT rslot", 0, rslot);
if (rslot < 0 || rslot >= wsize)
{
debug (DB_LOG, "HANDLE_RPKT error 1, rslot", 0, rslot);
debug (DB_LOG, "HANDLE_RPKT iseq", 0, iseq);
return (X_ERROR);
}
if (k->ipktinfo[rslot].flg == 0)
{
debug (DB_MSG, "HANDLE_RPKT error 2, flg", 0,
k->ipktinfo[rslot].flg);
debug (DB_LOG, " rslot", 0, rslot);
return (X_ERROR);
}
/* Decode pkt and write file */
rc = decode (k, r, 1, k->ipktinfo[rslot].buf, rslot);
debug (DB_LOG, "HANDLE_RPKT decode rc", 0, rc);
if (rc != X_OK)
{
debug (DB_LOG, "decode failed rc", 0, rc);
#ifdef DEBUG
show_rslots (k);
#endif
// exit (1);
epkt ("EKSW decode failed", k);
return (rc);
}
free_rslot (k, rslot);
#ifdef DEBUG
show_rslots (k);
#endif
}
}
do_add_pkt = 0;
if (isnxt)
{
// 1. PSN = SEQ = HIGH + 1, the usual case
debug (DB_LOG, "HANDLE_RPKT usual rseq", 0, rseq);
// acknowledge incoming packet
ack (k, rseq, (UCHAR *) 0);
do_add_pkt = 1;
}
else if (isgap)
{
// 2. PSN != SEQ, a new packet, but not the next sequential one
debug (DB_LOG, "HANDLE_RPKT isgap rseq", 0, rseq);
nnak = rseq - high - 1;
if (nnak < 0)
nnak += 64;
debug (DB_LOG, " nnak", 0, nnak);
// acknowledge incoming packet
ack (k, rseq, (UCHAR *) 0);
// send NAKs for whole gap: HIGH+1 to PSN-1
for (i = 0; i < nnak; i++)
{
iseq = (high + 1 + i) & 63;
rslot = k->r_pw[iseq];
debug (DB_LOG, "HANDLE_RPKT isgap nak iseq", 0, iseq);
debug (DB_LOG, " rslot", 0, rslot);
nak (k, iseq, rslot);
}
do_add_pkt = 2;
}
else if (isold)
{
// 3. LOW <= PSN <= HIGH, old, possibly missing pkt arrived
debug (DB_LOG, "HANDLE_RPKT isold rseq", 0, rseq);
ack (k, rseq, (UCHAR *) 0);
// If packet was missing add it to window.
// There is supposed to be a slot available.
// Don't store a repeated packet again
rslot = k->r_pw[rseq];
if (rslot < 0 || rslot >= wsize)
{
do_add_pkt = 3;
}
}
else
{
// 4. PSN < LOW || PSN > HIGH -- unexpected, undesired, ignore
debug (DB_LOG, "HANDLE_RPKT unexpected so ignored rseq", 0, rseq);
}
if (do_add_pkt > 0)
{
test_rslots (k);
get_rslot (k, &rslot);
if (rslot < 0 || rslot >= wsize)
{
debug (DB_LOG, "HANDLE_RPKT error 3, rslot", 0, rslot);
debug (DB_LOG, " do_add_pkt", 0, do_add_pkt);
return (X_ERROR);
}
test_rslots (k);
// put pbuf into slot in receive table
for (i = 0; i < P_BUFLEN; i++)
{
k->ipktinfo[rslot].buf[i] = pbuf[i];
if (pbuf[i] == 0)
break;
}
k->ipktinfo[rslot].len = i;
k->ipktinfo[rslot].flg = 1;
k->ipktinfo[rslot].seq = rseq;
k->ipktinfo[rslot].crc = chk3 (pbuf, k);
k->r_pw[rseq] = rslot;
test_rslots (k);
}
#ifdef DEBUG
show_rslots (k);
#endif
return (X_OK);
}
void
handle_bad_rpkt ()
{
}
int
ok2rxd (struct k_data *k)
{
int ok;
int sw_full;
if (nused_sslots (k) == k->wslots)
sw_full = 1;
else
sw_full = 0;
if ((k->what == W_SEND || k->what == W_GET) &&
k->state == S_DATA && sw_full == 0)
ok = 0;
else
ok = 1;
debug (DB_LOG, "ok2rxd", 0, ok);
k->do_rxd = ok;
return (ok);
}
STATIC void
free_sslot_easca (struct k_data *k)
// Remove earliest & subsequent contiguous ACK'd packets.
// Packet numbers thus remain in order in the sliding window.
// This way we don't send packets which won't fit in their window.
{
int nfreed = 0;
int i;
for (i = 0; i < k->wslots; i++)
{
short seq_earl, slot_earl;
seq_earl = earliest_sseq (k);
if (seq_earl < 0 || seq_earl > 63)
break;
slot_earl = k->s_pw[seq_earl];
if (slot_earl < 0 || slot_earl >= k->wslots)
break;
if (k->opktinfo[slot_earl].flg == 0)
break;
free_sslot (k, slot_earl);
nfreed++;
}
debug (DB_LOG, "FREE_SSLOT_EASCA nfreed", 0, nfreed);
}
#ifdef DEBUG
STATIC void
chk_sseq_nos (struct k_data *k)
// check that sequence numbers are still in order
{
int i;
int dif, dif0;
int ok;
int nerr;
nerr = 0;
ok = 0;
for (i = 0; i < k->wslots; i++)
{
short ss;
if (k->opktinfo[i].seq < 0)
continue;
ss = k->opktinfo[i].seq - k->r_seq;
if (ss < -32)
ss += 64;
if (ss > 31)
ss -= 64;
dif = ss - i;
if (ok == 0)
{
ok = 1;
dif0 = dif;
}
else if (dif != dif0 && (dif + k->wslots) != dif0)
nerr++;
}
if (nerr)
{
debug (DB_LOG, "CHK_SEQ nerr", 0, nerr);
#ifdef DEBUG
show_sslots (k);
#endif
}
}
#endif
STATIC int
flush_to_file (struct k_data *k, struct k_response *r)
{
short wsize = k->wslots; // negotiated window size
short rslot;
short iseq;
int rc;
debug (DB_MSG, "FLUSH_TO_FILE begin", 0, 0);
// flush receive window to file
while ((iseq = earliest_rseq (k)) >= 0)
{
debug (DB_LOG, "FLUSH_TO_FILE iseq", 0, iseq);
rslot = k->r_pw[iseq];
debug (DB_LOG, " rslot", 0, rslot);
if (rslot < 0 || rslot >= wsize)
{
debug (DB_LOG, "FLUSH_TO_FILE error rslot", 0, rslot);
return (X_ERROR);
}
if (k->ipktinfo[rslot].flg == 0)
{
debug (DB_MSG, "FLUSH_TO_FILE error flg", 0, k->ipktinfo[rslot].flg);
return (X_ERROR);
}
/* Decode pkt and write file */
rc = decode (k, r, 1, k->ipktinfo[rslot].buf, rslot);
debug (DB_LOG, "FLUSH_TO_FILE decode rc", 0, rc);
if (rc != X_OK)
{
debug (DB_LOG, "decode failed rc", 0, rc);
#ifdef DEBUG
show_rslots (k);
#endif
// exit (1);
epkt ("EKSW flush to file failed", k);
return (rc);
}
free_rslot (k, rslot);
#ifdef DEBUG
show_rslots (k);
#endif
}
debug (DB_LOG, "FLUSH_TO_FILE obufpos", 0, k->obufpos);
if (k->obufpos > 0)
{ /* Flush output buffer to file */
rc = (*(k->writef)) (k, k->obuf, k->obufpos);
debug (DB_LOG, "FLUSH_TO_FILE writef rc", 0, rc);
r->sofar += k->obufpos;
r->sofar_rumor += k->obufpos;
k->obufpos = 0;
}
return (rc);
}
/*
* Utility routines
*/
UCHAR *
get_rslot (struct k_data * k, short *n)
{ /* Find a free packet buffer */
register int slot;
/*
* Note: We don't clear the retry count here. It is cleared only after
* the NEXT packet arrives, which indicates that the other Kermit got
* our ACK for THIS packet.
*/
for (slot = 0; slot < k->wslots; slot++)
{ /* Search */
if (k->ipktinfo[slot].len < 1)
{
*n = slot; /* Slot number */
k->ipktinfo[slot].len = -1; /* Mark it as allocated but not used */
k->ipktinfo[slot].seq = -1;
k->ipktinfo[slot].typ = SP;
k->ipktinfo[slot].crc = 0xFFFF;
/*
* k->ipktinfo[slot].rtr = 0;
*//*
* (see comment above)
*/
k->ipktinfo[slot].dat = (UCHAR *) 0;
debug (DB_LOG, "GET_RSLOT slot", 0, slot);
return (k->ipktinfo[slot].buf);
}
}
*n = -1;
debug (DB_LOG, "GET_RSLOT slot", 0, -1);
return ((UCHAR *) 0);
}
void /* Initialize a window slot */
free_rslot (struct k_data *k, short slot)
{
short seq;
debug (DB_LOG, "FREE_RSLOT slot", 0, slot);
if (slot < 0 || slot >= P_WSLOTS)
{
debug (DB_LOG, " confused: slot out of bounds", 0, slot);
epkt ("EKSW free_rslot confused", k);
// exit (1);
return;
}
seq = k->ipktinfo[slot].seq;
debug (DB_LOG, " seq", 0, seq);
if (seq >= 0 && seq < 64)
k->r_pw[seq] = -1;
k->ipktinfo[slot].len = 0; /* Packet length */
k->ipktinfo[slot].seq = -1; /* Sequence number */
k->ipktinfo[slot].typ = (char) 0; /* Type */
k->ipktinfo[slot].rtr = 0; /* Retry count */
k->ipktinfo[slot].flg = 0; /* Flags */
k->ipktinfo[slot].crc = 0xFFFF;
}
UCHAR *
get_sslot (struct k_data *k, short *n)
{ /* Find a free packet buffer */
register int slot;
for (slot = 0; slot < k->wslots; slot++)
{ /* Search */
if (k->opktinfo[slot].len < 1)
{
*n = slot; /* Slot number */
k->opktinfo[slot].len = -1; /* Mark it as allocated but not used */
k->opktinfo[slot].seq = -1;
k->opktinfo[slot].typ = SP;
k->opktinfo[slot].rtr = 0;
k->opktinfo[slot].flg = 0; // ACK'd bit
k->opktinfo[slot].dat = (UCHAR *) 0;
debug (DB_LOG, "GET_SSLOT slot", 0, slot);
return (k->opktinfo[slot].buf);
}
}
*n = -1;
debug (DB_LOG, "GET_SSLOT slot", 0, -1);
return ((UCHAR *) 0);
}
STATIC int
nused_sslots (struct k_data *k)
{
int i;
int n = 0;
for (i = 0; i < k->wslots; i++)
if (k->opktinfo[i].len > 0)
n++;
return (n);
}
STATIC int
nused_rslots (struct k_data *k)
{
int i;
int n = 0;
for (i = 0; i < k->wslots; i++)
if (k->ipktinfo[i].len > 0)
n++;
return (n);
}
#ifdef DEBUG
STATIC void
show_sslots (struct k_data *k)
{
int slot, j, n;
char tmp[80], *p;
debug (DB_MSG, "SHOW_SSLOTS", 0, 0);
for (slot = 0; slot < k->wslots; slot++)
{
n = snprintf (tmp, sizeof (tmp) - 1, " slot=%02d seq=%02d flg=%d "
"len=%4d crc=%5d pwi=",
slot, k->opktinfo[slot].seq, k->opktinfo[slot].flg,
k->opktinfo[slot].len, k->opktinfo[slot].crc);
p = tmp + n;
for (j = 0; j < 64; j++)
if (k->s_pw[j] == slot)
{
n = snprintf (p, sizeof (tmp) - 1 - n, " %02d", j);
p += n;
}
debug (DB_MSG, tmp, 0, 0);
}
}
#endif /* DEBUG */
#ifdef DEBUG
STATIC void
show_rslots (struct k_data *k)
{
int slot, j, n, m;
char tmp[256], *p;
debug (DB_MSG, "SHOW_RSLOTS", 0, 0);
for (slot = 0; slot < k->wslots; slot++)
{
int ok;
if (k->ipktinfo[slot].seq >= 0 &&
chk3 (k->ipktinfo[slot].buf, k) != k->ipktinfo[slot].crc)
ok = 0;
else
ok = 1;
p = tmp;
m = sizeof (tmp) - 1;
n = snprintf (p, m, " slot=%02d seq=%02d flg=%d "
"len=%4d crc=%5d crcok=%d pwi=",
slot, k->ipktinfo[slot].seq, k->ipktinfo[slot].flg,
k->ipktinfo[slot].len, k->ipktinfo[slot].crc, ok);
if (n < 0)
{
debug (DB_LOG, "SHOW_RSLOTS 1 n", 0, n);
debug (DB_LOG, "SHOW_RSLOTS 1 m", 0, m);
debug (DB_LOG, "SHOW_RSLOTS 1 slot", 0, slot);
debug (DB_LOG, "SHOW_RSLOTS 1 seq", 0, k->ipktinfo[slot].seq);
debug (DB_LOG, "SHOW_RSLOTS 1 flg", 0, k->ipktinfo[slot].flg);
debug (DB_LOG, "SHOW_RSLOTS 1 len", 0, k->ipktinfo[slot].len);
debug (DB_LOG, "SHOW_RSLOTS 1 crc", 0, k->ipktinfo[slot].crc);
debug (DB_LOG, "SHOW_RSLOTS ok", 0, ok);
debug (DB_LOG, "SHOW_RSLOTS 1 p-tmp", 0, p - tmp);
debug (DB_LOG, "SHOW_RSLOTS 1 tmp", tmp, 0);
// exit (1);
}
p += n;
m -= n;
for (j = 0; j < 64; j++)
{
if (k->r_pw[j] == slot)
{
n = snprintf (p, m, " %02d", j);
if (n < 0)
{
debug (DB_LOG, "SHOW_RSLOTS 2 j", 0, j);
debug (DB_LOG, "SHOW_RSLOTS 2 n", 0, n);
debug (DB_LOG, "SHOW_RSLOTS 2 m", 0, m);
debug (DB_LOG, "SHOW_RSLOTS 2 slot", 0, slot);
debug (DB_LOG, "SHOW_RSLOTS 2 seq", 0, k->ipktinfo[slot].seq);
debug (DB_LOG, "SHOW_RSLOTS 2 flg", 0, k->ipktinfo[slot].flg);
debug (DB_LOG, "SHOW_RSLOTS 2 len", 0, k->ipktinfo[slot].len);
debug (DB_LOG, "SHOW_RSLOTS 2 crc", 0, k->ipktinfo[slot].crc);
debug (DB_LOG, "SHOW_RSLOTS 2 p-tmp", 0, p - tmp);
debug (DB_LOG, "SHOW_RSLOTS 2 tmp", tmp, 0);
// exit (1);
}
p += n;
m -= n;
}
}
debug (DB_MSG, tmp, 0, 0);
}
}
#endif
STATIC int
test_rslots (struct k_data *k)
{
int slot, nbad = 0;
for (slot = 0; slot < k->wslots; slot++)
{
if (k->ipktinfo[slot].seq >= 0 &&
chk3 (k->ipktinfo[slot].buf, k) != k->ipktinfo[slot].crc)
{
nbad++;
debug (DB_HEX, "THEX", k->ipktinfo[slot].buf, k->ipktinfo[slot].len);
}
}
if (nbad > 0)
{
debug (DB_LOG, "TEST_RSLOTS nbad", 0, nbad);
#ifdef DEBUG
show_rslots (k);
# endif
epkt ("EKSW test rslots failed", k);
// exit (1);
return X_ERROR;
}
return X_OK;
}
void /* Initialize a window slot */
free_sslot (struct k_data *k, short slot)
{
short seq;
debug (DB_LOG, "FREE_SSLOT slot", 0, slot);
if (slot < 0 || slot >= P_WSLOTS)
return;
seq = k->opktinfo[slot].seq;
debug (DB_LOG, " seq", 0, seq);
if (seq >= 0 && seq < 64)
k->s_pw[seq] = -1;
k->opktinfo[slot].len = 0; /* Packet length */
k->opktinfo[slot].seq = -1; /* Sequence number */
k->opktinfo[slot].typ = (char) 0; /* Type */
k->opktinfo[slot].rtr = 0; /* Retry count */
k->opktinfo[slot].flg = 0; /* ACK'd bit */
}
STATIC short
earliest_sseq (struct k_data *k)
{
short slot;
short seq;
short age;
short oldest_seq = -1;
short oldest_age = -99;
for (slot = 0; slot < k->wslots; slot++)
{
seq = k->opktinfo[slot].seq;
if (seq >= 0 && seq < 64)
{
age = k->s_seq - seq;
if (age < 0)
age += 64;
if (age > 63)
age -= 64;
if (age > oldest_age)
{
oldest_age = age;
oldest_seq = seq;
}
}
}
debug (DB_LOG, "EARLIEST_SSEQ", 0, oldest_seq);
return (oldest_seq);
}
/*
* C H K 1 -- Compute a type-1 Kermit 6-bit checksum.
*/
STATIC int
chk1 (UCHAR * pkt, struct k_data *k)
{
register unsigned int chk;
chk = chk2 (pkt, k);
chk = (((chk & 0300) >> 6) + chk) & 077;
return ((int) chk);
}
/*
* C H K 2 -- Numeric sum of all the bytes in the packet, 12 bits.
*/
STATIC USHORT
chk2 (UCHAR * pkt, struct k_data *k)
{
register USHORT chk;
for (chk = 0; *pkt != '\0'; pkt++)
chk += *pkt;
return (chk);
}
/*
* C H K 3 -- Compute a type-3 Kermit block check.
*/
/*
* Calculate the 16-bit CRC-CCITT of a null-terminated string using a
* lookup table. Assumes the argument string contains no embedded nulls.
*/
STATIC USHORT
chk3 (UCHAR * pkt, struct k_data * k)
{
register USHORT c, crc;
for (crc = 0; *pkt != '\0'; pkt++)
{
c = crc ^ (*pkt);
crc = (crc >> 8) ^ ((k->crcta[(c & 0xF0) >> 4]) ^ (k->crctb[c & 0x0F]));
}
return (crc);
}
/*
* S P K T -- Send a packet.
*/
/*
* Call with packet type, sequence number, data length, data, Kermit
* struct. Returns: X_OK on success X_ERROR on i/o error
*/
STATIC int
spkt (char typ, short seq, int len, UCHAR * data, struct k_data *k)
{
int retc;
unsigned int crc; /* For building CRC */
int i, j, lenpos; /* Workers */
UCHAR *s, *buf;
int buflen;
short slot;
UCHAR tmp[100]; // for packets we don't want to resend
debug (DB_CHR, "SPKT typ", 0, typ);
debug (DB_LOG, " seq", 0, seq);
debug (DB_LOG, " len", 0, len);
if (seq < 0 || seq > 63)
return (X_ERROR);
if (len < 0)
{ /* Calculate data length ourselves? */
len = 0;
s = data;
while (*s++)
len++;
debug (DB_LOG, "SPKT calc len", 0, len);
}
if (typ == 'Y' || typ == 'N' || typ == 'E')
{
buf = tmp;
buflen = sizeof (tmp);
}
else
{
debug (DB_LOG, "SPKT k->s_seq", 0, k->s_seq);
debug (DB_LOG, "SPKT k->s_pw[seq]", 0, k->s_pw[seq]);
get_sslot (k, &slot); // get a new send slot
if (slot < 0 || slot >= k->wslots)
return (X_ERROR);
k->s_pw[k->s_seq] = slot;
// save these for use in resend()
k->opktinfo[slot].typ = typ;
k->opktinfo[slot].seq = seq;
k->opktinfo[slot].len = len;
buf = k->opktinfo[slot].buf;
buflen = P_BUFLEN;
}
i = 0; /* Packet buffer position */
buf[i++] = k->s_soh; /* SOH */
lenpos = i++; /* Remember this place */
buf[i++] = tochar (seq); /* Sequence number */
buf[i++] = typ; /* Packet type */
if (k->bcta3)
k->bct = 3;
j = len + k->bct;
if ((len + k->bct + 2) > 94)
{ /* If long packet */
buf[lenpos] = tochar (0); /* Put blank in LEN field */
buf[i++] = tochar (j / 95); /* Make extended header: Big part */
buf[i++] = tochar (j % 95); /* and small part of length. */
buf[i] = NUL; /* Terminate for header checksum */
buf[i++] = tochar (chk1 (&buf[lenpos], k)); /* Insert header checksum */
}
else
{ /* Short packet */
buf[lenpos] = tochar (j + 2); /* Single-byte length in LEN field */
}
if (data) /* Copy data, if any */
for (; len--; i++)
{
if (i < 0 || i >= buflen)
{
debug (DB_LOG, "confused copy data i", 0, i);
epkt ("EKSW spkt confused", k);
// exit (1);
return (X_ERROR);
}
buf[i] = *data++;
}
buf[i] = '\0';
switch (k->bct)
{ /* Add block check */
case 1: /* 1 = 6-bit chksum */
buf[i++] = tochar (chk1 (&buf[lenpos], k));
break;
case 2: /* 2 = 12-bit chksum */
j = chk2 (&buf[lenpos], k);
#if 0
buf[i++] = (unsigned) tochar ((j >> 6) & 077);
buf[i++] = (unsigned) tochar (j & 077);
#else
// HiTech's XAC compiler silently ruins the above code.
// An intermediate variable provides a work-around.
// 2004-06-29 -- JHD
{
USHORT jj;
jj = (j >> 6) & 077;
buf[i++] = tochar (jj);
jj = j & 077;
buf[i++] = tochar (jj);
}
#endif
break;
case 3: /* 3 = 16-bit CRC */
crc = chk3 (&buf[lenpos], k);
#if 0
buf[i++] = (unsigned) tochar (((crc & 0170000)) >> 12);
buf[i++] = (unsigned) tochar ((crc >> 6) & 077);
buf[i++] = (unsigned) tochar (crc & 077);
#else
// HiTech's XAC compiler silently ruins the above code.
// An intermediate variable provides a work-around.
// 2004-06-29 -- JHD
{
USHORT jj;
jj = (crc >> 12) & 0x0f;
buf[i++] = tochar (jj);
jj = (crc >> 6) & 0x3f;
buf[i++] = tochar (jj);
jj = crc & 0x3f;
buf[i++] = tochar (jj);
}
#endif
break;
}
buf[i++] = k->s_eom; /* Packet terminator */
buf[i] = '\0'; /* String terminator */
// packet ready to go
if (i < 0 || i >= buflen)
{
debug (DB_LOG, "SPKT i", 0, i);
debug (DB_LOG, "SPKT buflen", 0, buflen);
return (X_ERROR);
}
k->s_seq = seq; /* Remember sequence number */
k->opktlen = i; /* Remember length for retransmit */
if (typ != 'Y' && typ != 'N' && typ != 'E')
{
k->opktinfo[slot].len = i; /* Remember length for retransmit */
}
debug (DB_LOG, "SPKT opktlen", 0, k->opktlen);
#ifdef DEBUG
/*
* CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE
*/
if (xerror ())
{
UCHAR p[P_BUFLEN];
int i;
for (i = 0; i < buflen - 8; i++)
if (!(p[i] = buf[i]))
break;
if (xerror ())
{
p[i - 2] = 'X';
debug (DB_PKT, "XPKT", (char *) &p[1], 0);
}
else if (xerror ())
{
p[k->opktlen - 1] = 'N';
debug (DB_PKT, "NPKT", (char *) &p[1], 0);
}
else
{
p[0] = 'A';
debug (DB_PKT, "APKT", (char *) &p[1], 0);
}
return ((*(k->txd)) (k, p, k->opktlen)); /* Send it. */
}
#endif /* DEBUG */
// debug (DB_PKT, "SPKT", &buf[1], k->opktlen);
retc = ((*(k->txd)) (k, buf, k->opktlen)); /* Send buf == whole packet */
debug (DB_LOG, "SPKT txd retc", 0, retc);
return (retc);
}
/*
* N A K -- Send a NAK (negative acknowledgement)
*/
STATIC int
nak (struct k_data *k, short seq, short slot)
{
int rc;
k->nsnak++;
debug (DB_LOG, "NAK seq", 0, seq);
debug (DB_LOG, " slot", 0, slot);
// k->anseq = seq;
rc = spkt ('N', seq, 0, (UCHAR *) 0, k);
if (slot >= 0 && slot < 64 && k->ipktinfo[slot].rtr++ > k->retry)
{
debug (DB_MSG, "X_ERROR returned from nak(): too many retries", 0, 0);
debug (DB_MSG, " seq", 0, seq);
debug (DB_MSG, " slot", 0, slot);
rc = X_ERROR;
}
return (rc);
}
/*
* A C K -- Send an ACK (positive acknowledgement)
*/
STATIC int
ack (struct k_data *k, short seq, UCHAR * text)
{
int len, rc;
debug (DB_LOG, "ACK seq", 0, seq);
// k->anseq = (seq+1)&63;
len = 0;
if (text)
{ /* Get length of data */
UCHAR *p;
p = text;
for (; *p++; len++);
}
debug (DB_LOG, "ACK len", 0, len);
rc = spkt ('Y', seq, len, text, k); /* Send the packet */
debug (DB_LOG, "ACK spkt rc", 0, rc);
if (rc == X_OK && seq == k->r_seq) /* If OK */
k->r_seq = (k->r_seq + 1) & 63; /* bump the packet number */
debug (DB_LOG, "ACK new k->r_seq", 0, k->r_seq);
return (rc);
}
/*
* S P A R -- Set parameters requested by other Kermit
*/
STATIC void
spar (struct k_data *k, UCHAR * s, int datalen)
{
int x;
int y = 0;
debug (DB_MSG, "SPAR", 0, 0);
s--; /* Line up with field numbers. */
if (datalen >= 1) /* Max packet length to send */
k->s_maxlen = xunchar (s[1]);
if (datalen >= 2) /* Timeout on inbound packets */
k->r_timo = xunchar (s[2]);
/*
* No padding
*/
if (datalen >= 5) /* Outbound Packet Terminator */
k->s_eom = xunchar (s[5]);
if (datalen >= 6) /* Incoming control prefix */
k->r_ctlq = s[6];
if (datalen >= 7)
{ /* 8th bit prefix */
k->ebq = s[7];
if ((s[7] > 32 && s[7] < 63) || (s[7] > 95 && s[7] < 127))
{
if (!k->parity) /* They want it */
k->parity = 1; /* Set parity to something nonzero */
k->ebqflg = 1;
}
else if (s[7] == 'Y' && k->parity)
{ // they will do 8th bit prefixing if requested
k->ebqflg = 1;
k->ebq = '&';
}
else if (s[7] == 'N')
{ // they refuse to do 8th bit prefixing
/*
* WHAT?
*/
}
}
if (datalen >= 8)
{ /* Block check */
k->bct = s[8] - '0';
if ((k->bct < 1) || (k->bct > 3))
k->bct = 1;
if (k->bcta3)
k->bct = 3;
}
if (datalen >= 9)
{ /* Repeat counts */
if ((s[9] > 32 && s[9] < 63) || (s[9] > 95 && s[9] < 127))
{
k->rptq = s[9];
k->rptflg = 1;
}
}
if (datalen >= 10)
{ /* Capability bits */
x = xunchar (s[10]);
if (!(x & CAP_LP))
k->capas &= ~CAP_LP;
if (!(x & CAP_SW))
k->capas &= ~CAP_SW;
if (!(x & CAP_AT))
k->capas &= ~CAP_AT;
#ifdef F_RS /* Recovery */
if (!(x & CAP_RS))
#endif /* F_RS */
k->capas &= ~CAP_RS;
#ifdef F_LS /* Locking shifts */
if (!(x & CAP_LS))
#endif /* F_LS */
k->capas &= ~CAP_LS;
/* In case other Kermit sends addt'l capas fields ... */
for (y = 10; (xunchar (s[y]) & 1) && (datalen >= y); y++);
}
if (k->capas & CAP_LP)
{
if (datalen > y + 1)
{
x = xunchar (s[y + 2]) * 95 + xunchar (s[y + 3]);
k->s_maxlen = (x > P_PKTLEN) ? P_PKTLEN : x;
if (k->s_maxlen < 10)
k->s_maxlen = 60;
}
}
debug (DB_LOG, " s_maxlen", 0, k->s_maxlen);
if (k->capas & CAP_SW)
{
if (datalen > y)
{
x = xunchar (s[y + 1]);
k->wslots = (x > k->wslots_max) ? k->wslots_max : x;
if (k->wslots < 1) /* Watch out for bad negotiation */
k->wslots = 1;
if (k->wslots > 1)
if (k->wslots > k->retry) /* Retry limit must be greater */
k->retry = k->wslots + 1; /* than window size. */
}
}
else
{
k->wslots = 1;
}
debug (DB_LOG, " k->capas & CAP_LP", 0, k->capas & CAP_LP);
debug (DB_LOG, " k->capas & CAP_SW", 0, k->capas & CAP_SW);
debug (DB_LOG, " k->capas & CAP_AT", 0, k->capas & CAP_AT);
debug (DB_LOG, " k->capas & CAP_RS", 0, k->capas & CAP_RS);
debug (DB_LOG, " k->capas & CAP_LS", 0, k->capas & CAP_LS);
debug (DB_CHR, " k->ebq ", 0, k->ebq);
debug (DB_LOG, " k->ebqflg ", 0, k->ebqflg);
debug (DB_LOG, " k->parity ", 0, k->parity);
debug (DB_LOG, " k->s_eom ", 0, k->s_eom);
debug (DB_LOG, " k->r_timo ", 0, k->r_timo);
debug (DB_LOG, " k->s_timo ", 0, k->s_timo);
debug (DB_CHR, " k->r_ctlq ", 0, k->r_ctlq);
debug (DB_CHR, " k->s_ctlq ", 0, k->s_ctlq);
debug (DB_CHR, " k->rptq ", 0, k->rptq);
debug (DB_LOG, " k->rptflg ", 0, k->rptflg);
debug (DB_LOG, " k->bct ", 0, k->bct);
debug (DB_LOG, " k->bcta3 ", 0, k->bcta3);
debug (DB_LOG, " k->r_maxlen ", 0, k->r_maxlen);
debug (DB_LOG, " k->s_maxlen ", 0, k->s_maxlen);
debug (DB_LOG, " k->wslots ", 0, k->wslots);
debug (DB_LOG, " k->binary ", 0, k->binary);
debug (DB_LOG, " k->retry ", 0, k->retry);
}
/*
* R P A R -- Send my parameters to other Kermit
*/
STATIC int
rpar (struct k_data *k, char type)
{
UCHAR *d;
int rc, len;
short bctsv;
UCHAR *buf;
short s_slot;
debug (DB_LOG, "RPAR capas", 0, k->capas);
debug (DB_LOG, " wslots", 0, k->wslots);
d = k->ack_s; /* Where to put it */
d[0] = tochar (94); /* Maximum short-packet length */
d[1] = tochar (k->s_timo); /* When I want to be timed out */
d[2] = tochar (0); /* How much padding I need */
d[3] = ctl (0); /* Padding character I want */
d[4] = tochar (k->r_eom); /* End-of message character I want */
d[5] = k->s_ctlq; /* Control prefix I send */
if ((k->ebq == 'Y') && (k->parity)) /* 8th-bit prefix */
d[6] = k->ebq = '&'; /* I need to request it */
else /* else just agree with other Kermit */
d[6] = k->ebq;
if (k->bcta3)
d[7] = '3';
else
d[7] = k->bct + '0'; /* Block check type */
d[8] = k->rptq; /* Repeat prefix */
d[9] = tochar (k->capas); /* Capability bits */
d[10] = tochar (k->wslots); /* Window size */
d[11] = tochar (k->r_maxlen / 95); /* Long packet size, big part */
d[12] = tochar (k->r_maxlen % 95); /* Long packet size, little part */
d[13] = '\0'; /* Terminate the init string */
len = 13;
if (k->bcta3)
{
bctsv = 3;
k->bct = 3;
}
else
{
bctsv = k->bct;
k->bct = 1; /* Always use block check type 1 */
}
switch (type)
{
case 'Y': /* This is an ACK for packet 0 */
rc = ack (k, 0, d);
break;
case 'S': /* It's an S packet */
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "sending S pkt s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
rc = spkt ('S', 0, len, d, k);
break;
default:
rc = -1;
}
k->bct = bctsv;
return (rc); /* Pass along return code. */
}
/*
* D E C O D E -- Decode data field of Kermit packet - binary mode only
*/
/*
Call with:
k = kermit data structure
r = kermit response structure
f = function code:
0: decode filename
1: decode file data
inbuf = pointer to packet data to be decoded
Returns: X_OK on success X_ERROR if output function fails
*/
STATIC int
decode (struct k_data *k, struct k_response *r, short f, UCHAR * inbuf,
int rslot)
{
register unsigned int a, a7; /* Current character */
unsigned int b8; /* 8th bit */
int rpt; /* Repeat count */
int rc; /* Return code */
UCHAR *ucp = 0;
int i;
int nobuf;
USHORT crc;
rc = X_OK;
rpt = 0; /* Initialize repeat count. */
if (f == 0) /* Output function... */
ucp = r->filename;
debug (DB_LOG, "DECODE rslot", 0, rslot);
if (rslot >= 0)
{
k->anseq = k->ipktinfo[rslot].seq;
debug (DB_LOG, "DECODE seq", 0, k->ipktinfo[rslot].seq);
debug (DB_LOG, "DECODE len", 0, k->ipktinfo[rslot].len);
debug (DB_LOG, "DECODE crc", 0, k->ipktinfo[rslot].crc);
debug (DB_HEX, "IHEX", inbuf, k->ipktinfo[rslot].len);
for (i = 0; i < k->ipktinfo[rslot].len; i++)
if (inbuf[i] == 0)
break;
if (i != k->ipktinfo[rslot].len)
{
debug (DB_LOG, "DECODE error i", 0, i);
#ifdef DEBUG
show_rslots (k);
#endif
epkt ("EKSW decode len bad", k);
// exit (1);
return (X_ERROR);
}
crc = chk3 (inbuf, k);
if (crc != k->ipktinfo[rslot].crc)
{
debug (DB_LOG, "DECODE error crc", 0, crc);
#ifdef DEBUG
show_rslots (k);
#endif
epkt ("EKSW decode crc bad", k);
// exit (1);
return (X_ERROR);
}
}
nobuf = 0;
while ((a = *inbuf++ & 0xFF) != '\0')
{ /* Character loop */
if (k->rptflg && a == k->rptq)
{ /* Got a repeat prefix? */
rpt = xunchar (*inbuf++ & 0xFF); /* Yes, get the repeat count, */
a = *inbuf++ & 0xFF; /* and get the prefixed character. */
}
b8 = 0; /* 8th-bit value */
if (k->parity && (a == k->ebq))
{ /* Have 8th-bit prefix? */
b8 = 0200; /* Yes, flag the 8th bit */
a = *inbuf++ & 0x7F; /* and get the prefixed character. */
}
if (a == k->r_ctlq)
{ /* If control prefix, */
a = *inbuf++ & 0xFF; /* get its operand */
a7 = a & 0x7F; /* and its low 7 bits. */
if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Controllify */
a = ctl (a); /* if in control range. */
}
a |= b8; /* OR in the 8th bit */
// debug(DB_LOG,"DECODE rpt",0,rpt);
if (rpt == 0)
rpt = 1; /* If no repeats, then one */
for (; rpt > 0; rpt--)
{ /* Output the char 'rpt' times */
if (f == 0)
{
*ucp++ = (UCHAR) a; /* to memory */
}
else
{ /* or to file */
k->obuf[k->obufpos++] = (UCHAR) a; /* Deposit the byte */
nobuf++;
if (k->obufpos == k->obuflen)
{ /* Buffer full? */
rc = (*(k->writef)) (k, k->obuf, k->obuflen); /* Dump it. */
debug (DB_LOG, "DECODE writef rc", 0, rc);
r->sofar += k->obuflen;
r->sofar_rumor += k->obuflen;
if (rc != X_OK)
break;
k->obufpos = 0;
}
}
}
}
debug (DB_LOG, "DECODE nobuf", 0, nobuf);
debug (DB_LOG, "DECODE obufpos", 0, k->obufpos);
if (f == 0) /* If writing to memory */
*ucp = '\0'; /* terminate the string */
debug (DB_LOG, "DECODE rc", 0, rc);
return (rc);
}
STATIC ULONG /* Convert decimal string to number */
stringnum (UCHAR * s, struct k_data * k)
{
long n;
n = 0L;
while (*s == SP)
s++;
while (*s >= '0' && *s <= '9')
n = n * 10 + (*s++ - '0');
return (n);
}
STATIC UCHAR * /* Convert number to string */
numstring (ULONG n, UCHAR * buf, int buflen, struct k_data * k)
{
int i, x;
buf[buflen - 1] = '\0';
for (i = buflen - 2; i > 0; i--)
{
x = n % 10L;
buf[i] = x + '0';
n /= 10L;
if (!n)
break;
}
if (n)
{
return ((UCHAR *) 0);
}
if (i > 0)
{
UCHAR *p, *s;
s = &buf[i];
p = buf;
while ((*p++ = *s++));
*(p - 1) = '\0';
}
return ((UCHAR *) buf);
}
/*
* G A T T R -- Read incoming attributes.
*
* Returns: -1 if no transfer mode (text/binary) was announced. 0 if text
* was announced. 1 if binary was announced.
*/
#define SIZEBUFL 32 /* For number conversions */
STATIC int
gattr (struct k_data *k, UCHAR * s, struct k_response *r)
{
long fsize = 0, fsizek = 0; /* File size */
UCHAR c; /* Workers */
int aln, i, rc;
UCHAR sizebuf[SIZEBUFL];
rc = -1;
while ((c = *s++))
{ /* Get attribute tag */
aln = xunchar (*s++); /* Length of attribute string */
switch (c)
{
case '!': /* File length in K */
case '"': /* File type */
for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */
sizebuf[i] = *s++;
sizebuf[i] = '\0'; /* Terminate with null */
if (i < aln)
s += (aln - i); /* If field was too long for buffer */
if (c == '!')
{ /* Length */
fsizek = stringnum (sizebuf, k); /* Convert to number */
}
else
{ /* Type */
if (sizebuf[0] == 'A') /* Text */
rc = 0;
else if (sizebuf[0] == 'B') /* Binary */
rc = 1;
debug (DB_LOG, "GATTR rc", 0, rc);
debug (DB_LOG, "GATTR sizebuf", sizebuf, 0);
}
break;
case '#': /* File creation date */
for (i = 0; (i < aln) && (i < DATE_MAX); i++)
r->filedate[i] = *s++; /* save it to a string */
if (i < aln)
s += (aln - i);
r->filedate[i] = '\0';
break;
case '1': /* File length in bytes */
for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */
sizebuf[i] = *s++;
sizebuf[i] = '\0'; /* Terminate with null */
if (i < aln)
s += (aln - i);
fsize = stringnum (sizebuf, k); /* Convert to number */
break;
default: /* Unknown attribute */
s += aln; /* Just skip past it */
break;
}
}
if (fsize > -1L)
{ /* Remember the file size */
r->filesize = fsize;
}
else if (fsizek > -1L)
{
r->filesize = fsizek * 1024L;
}
debug (DB_LOG, "gattr r->filesize", 0, (r->filesize));
debug (DB_LOG, "gattr r->filedate=", r->filedate, 0);
return (rc);
}
#define ATTRLEN 48
STATIC int
sattr (struct k_data *k, struct k_response *r)
{ /* Build and send A packet */
// int i, x, aln;
int i, x;
short tmp;
long filelength;
UCHAR datebuf[DATE_MAX], *p;
UCHAR *buf;
short s_slot;
debug (DB_PKT, "SATTR k->zincnt 0", 0, (k->zincnt));
tmp = k->binary;
filelength = (*(k->finfo))
(k, k->filename, datebuf, DATE_MAX, &tmp, k->xfermode);
k->binary = tmp;
debug (DB_LOG, " filename: ", k->filename, 0);
debug (DB_LOG, " filedate: ", datebuf, 0);
debug (DB_LOG, " filelength", 0, filelength);
debug (DB_LOG, " binary", 0, (k->binary));
i = 0;
k->xdata[i++] = '"';
if (k->binary)
{ /* Binary */
k->xdata[i++] = tochar (2); /* Two characters */
k->xdata[i++] = 'B'; /* B for Binary */
k->xdata[i++] = '8'; /* 8-bit bytes (note assumption...) */
}
else
{ /* Text */
k->xdata[i++] = tochar (3); /* Three characters */
k->xdata[i++] = 'A'; /* A = (extended) ASCII with CRLFs */
k->xdata[i++] = 'M'; /* M for carriage return */
k->xdata[i++] = 'J'; /* J for linefeed */
k->xdata[i++] = '*'; /* Encoding */
k->xdata[i++] = tochar (1); /* Length of value is 1 */
k->xdata[i++] = 'A'; /* A for ASCII */
}
if (filelength > -1L)
{ /* File length in bytes */
UCHAR lenbuf[16];
r->filesize = filelength;
p = numstring (filelength, lenbuf, 16, k);
if (p)
{
for (x = 0; p[x]; x++); /* Get length of length string */
if (i + x < ATTRLEN - 3)
{ /* Don't overflow buffer */
k->xdata[i++] = '1'; /* Length-in-Bytes attribute */
k->xdata[i++] = tochar (x);
while (*p)
k->xdata[i++] = *p++;
}
}
}
debug (DB_LOG, "SATTR datebuf: ", datebuf, 0);
if (datebuf[0])
{ /* File modtime */
p = datebuf;
for (x = 0; p[x]; x++); /* Length of modtime */
if (i + x < ATTRLEN - 3)
{ /* If it will fit */
k->xdata[i++] = '#'; /* Add modtime attribute */
k->xdata[i++] = tochar (x); /* Its length */
while (*p) /* And itself */
k->xdata[i++] = *p++;
/*
* Also copy modtime to result struct
*/
for (x = 0; x < DATE_MAX - 1 && datebuf[x]; x++)
r->filedate[x] = datebuf[x];
r->filedate[x] = '\0';
}
}
k->xdata[i++] = '@'; /* End of Attributes */
k->xdata[i++] = ' ';
k->xdata[i] = '\0'; /* Terminate attribute string */
debug (DB_PKT, "SATTR k->xdata: ", k->xdata, 0);
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "SATTR sending A pkt s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
return (spkt ('A', k->s_seq, -1, k->xdata, k));
}
STATIC int
getpkt (struct k_data *k, struct k_response *r)
{ /* Fill a packet from file */
int i, next, rpt, maxlen;
// static int c; /* PUT THIS IN STRUCT */
int c = k->cgetpkt;
debug (DB_LOG, "GETPKT k->s_first", 0, k->s_first);
// debug (DB_PKT, " k->s_remain=", k->s_remain, 0);
if (k->bcta3)
k->bct = 3;
maxlen = k->s_maxlen - k->bct - 3 - 6; /* Maximum data length */
if (k->s_first == 1)
{ /* If first time thru... */
k->s_first = 0; /* don't do this next time, */
k->s_remain[0] = '\0'; /* discard any old leftovers. */
if (k->istring)
{ /* Get first byte. */
c = *(k->istring)++; /* Of memory string... */
if (!c)
c = -1;
}
else
{ /* or file... */
#ifdef USE_ZGETC_MACRO
c = zgetc ();
#else
c = zgetc (k);
#endif
}
k->cgetpkt = c;
if (c < 0)
{ /* Watch out for empty file. */
debug (DB_LOG, "GETPKT first c", 0, c);
k->s_first = -1;
return (k->size = 0);
}
// r->sofar++; // makes it too big
if (k->state == S_DATA)
r->sofar_rumor++;
debug (DB_LOG, "GETPKT first state", 0, k->state);
debug (DB_CHR, " first c", 0, c);
}
else if (k->s_first == -1 && !k->s_remain[0])
{ /* EOF from last time? */
return (k->size = 0);
}
// string copy s_remain to xdata
for (k->size = 0;
(k->xdata[k->size] = k->s_remain[k->size]) != '\0'; (k->size)++);
// set s_remain to zero length
k->s_remain[0] = '\0';
if (k->s_first == -1)
return (k->size);
rpt = 0; /* Initialize repeat counter. */
while (k->s_first > -1)
{ /* Until end of file or string... */
if (k->istring)
{
next = *(k->istring)++;
if (!next)
next = -1;
}
else
{
#ifdef USE_ZGETC_MACRO
next = zgetc ();
#else
next = zgetc (k);
#endif
}
if (next < 0)
{ /* If none, we're at EOF. */
k->s_first = -1;
}
else
{ /* Otherwise */
r->sofar_rumor++; /* count this byte */
}
k->osize = k->size; /* Remember current size. */
encode (c, next, k); /* Encode the character. */
/*
* k->xdata[k->size] = '\0';
*/
c = next; /* Old next char is now current. */
k->cgetpkt = c;
if (k->size == maxlen)
{ /* Just at end, done. */
debug (DB_LOG, "GETPKT size perfect c", 0, c);
return (k->size);
}
if (k->size > maxlen)
{ /* Past end, must save some. */
for (i = 0; (k->s_remain[i] = k->xdata[(k->osize) + i]) != '\0';
i++);
debug (DB_LOG, "GETPKT size past end i", 0, i);
k->size = k->osize;
k->xdata[k->size] = '\0';
return (k->size); /* Return size. */
}
}
if (k->size > maxlen)
{
debug (DB_LOG, "GETPKT error confused: from getpkt() k->size",
0, k->size);
epkt ("EKSW getpkt confused", k);
// exit (1);
return (X_ERROR);
}
return (k->size); /* EOF, return size. */
}
STATIC int
sdata (struct k_data *k, struct k_response *r)
{ /* Send a data packet */
int len, rc;
if (k->cancel)
{ /* Interrupted */
debug (DB_LOG, "SDATA interrupted k->cancel", 0, (k->cancel));
return (0);
}
len = getpkt (k, r); /* Fill data field from input file */
debug (DB_LOG, "SDATA getpkt len", 0, len);
if (len < 1)
{
debug (DB_LOG, "SDATA getpkt got eof s_seq", 0, k->s_seq);
return (0);
}
debug (DB_LOG, "SDATA sending D pkt s_seq", 0, k->s_seq);
rc = spkt ('D', k->s_seq, len, k->xdata, k); /* Send the packet */
debug (DB_LOG, "SDATA spkt rc", 0, rc);
return ((rc == X_ERROR) ? rc : len);
}
/*
* E P K T -- Send a (fatal) Error packet with the given message
*/
STATIC void
epkt (char *msg, struct k_data *k)
{
int bctsv;
if (k->bcta3)
{
bctsv = 3;
k->bct = 3;
}
else
{
bctsv = k->bct;
k->bct = 1;
}
debug (DB_LOG, "EPKT msg:", msg, 0);
spkt ('E', 0, -1, (UCHAR *) msg, k);
k->bct = bctsv;
}
STATIC int /* Fill a packet from string s. */
encstr (UCHAR * s, struct k_data *k, struct k_response *r)
{
k->s_first = 1; /* Start lookahead. */
k->istring = s; /* Set input string pointer */
getpkt (k, r); /* Fill a packet */
k->istring = (UCHAR *) 0; /* Reset input string pointer */
k->s_first = 1; /* "Rewind" */
return (k->size); /* Return data field length */
}
/*
* Decode packet data into a string
*/
#if 0 // never used
STATIC void
decstr (UCHAR * s, struct k_data *k, struct k_response *r)
{
k->ostring = s; /* Set output string pointer */
(void) decode (k, r, 0, s, -1);
*(k->ostring) = '\0'; /* Terminate with null */
k->ostring = (UCHAR *) 0; /* Reset output string pointer */
}
#endif
STATIC void
encode (int a, int next, struct k_data *k)
{ /* Encode character into packet == k->xdata */
int a7, b8, maxlen;
maxlen = k->s_maxlen - 4;
if (k->rptflg)
{ /* Doing run-length encoding? */
if (a == next)
{ /* Yes, got a run? */
if (++(k->s_rpt) < 94)
{ /* Yes, count. */
return;
}
else if (k->s_rpt == 94)
{ /* If at maximum */
k->xdata[(k->size)++] = k->rptq; /* Emit prefix, */
k->xdata[(k->size)++] = tochar (k->s_rpt); /* and count, */
k->s_rpt = 0; /* and reset counter. */
}
}
else if (k->s_rpt == 1)
{ /* Run broken, only two? */
k->s_rpt = 0; /* Yes, do the character twice */
encode (a, -1, k); /* by calling self recursively. */
if (k->size <= maxlen) /* Watch boundary. */
k->osize = k->size;
k->s_rpt = 0; /* Call self second time. */
encode (a, -1, k);
return;
}
else if (k->s_rpt > 1)
{ /* Run broken, more than two? */
k->xdata[(k->size)++] = k->rptq; /* Yes, emit prefix and count */
k->xdata[(k->size)++] = tochar (++(k->s_rpt));
k->s_rpt = 0; /* and reset counter. */
}
}
a7 = a & 127; /* Get low 7 bits of character */
b8 = a & 128; /* And "parity" bit */
if (k->ebqflg && b8)
{ /* If doing 8th bit prefixing */
k->xdata[(k->size)++] = k->ebq; /* and 8th bit on, insert prefix */
a = a7; /* and clear the 8th bit. */
}
// if (a7 < 32 || a7 == 127) /* If in control range -- conservative */
// if (a7==0 || a7==1 || a7==13) // 2004-07-04 -- JHD -- need 127 for telnet
// this is a bit more conservative than C-Kermit "set prefixing minimal"
if (a7 == 0 || a7 == 1 || a7 == 3 || a7 == 4 || a7 == 10 ||
a7 == 13 || a7 == 21 || a7 == 127)
{
k->xdata[(k->size)++] = k->s_ctlq; /* insert control prefix */
a = ctl (a); /* and make character printable. */
}
else if (a7 == k->s_ctlq) /* If data is control prefix, */
k->xdata[(k->size)++] = k->s_ctlq; /* prefix it. */
else if (k->ebqflg && a7 == k->ebq) /* If doing 8th-bit prefixing, */
k->xdata[(k->size)++] = k->s_ctlq; /* ditto for 8th-bit prefix. */
else if (k->rptflg && a7 == k->rptq) /* If doing run-length encoding, */
k->xdata[(k->size)++] = k->s_ctlq; /* ditto for repeat prefix. */
k->xdata[(k->size)++] = a; /* Finally, emit the character. */
k->xdata[(k->size)] = '\0'; /* Terminate string with null. */
if (k->size < 0 || k->size >= P_PKTLEN + 2)
{
debug (DB_LOG, "confused: from encode() k->size", 0, k->size);
epkt ("EKSW encode confused", k);
// exit (1);
return;
}
}
STATIC short
nxtpkt (struct k_data *k)
{ /* Get next packet to send */
k->s_seq = (k->s_seq + 1) & 63; /* Next sequence number */
k->s_cnt++;
k->xdata = k->xdatabuf;
return (k->s_seq);
}
STATIC int
resend (struct k_data *k, short seq)
{
UCHAR *buf;
int ret;
short slot;
debug (DB_LOG, "RESEND seq", 0, seq);
if (seq < 0)
seq = earliest_sseq (k);
if (seq < 0)
{
debug (DB_LOG, "RESEND failed: no earliest seq", 0, seq);
#ifdef DEBUG
show_sslots (k);
#endif
return (X_OK);
}
slot = k->s_pw[seq];
if (slot < 0)
{
debug (DB_LOG, "RESEND failed: no slot for seq", 0, seq);
#ifdef DEBUG
show_sslots (k);
#endif
seq = earliest_sseq (k);
if (seq < 0)
{
debug (DB_LOG, "RESEND failed: still no earliest seq", 0, seq);
#ifdef DEBUG
show_sslots (k);
#endif
return (X_OK);
}
slot = k->s_pw[seq];
debug (DB_LOG, "RESEND sending earliest seq", 0, seq);
debug (DB_LOG, " slot", 0, slot);
}
if (slot < 0)
{
debug (DB_LOG, "RESEND failed: still bad slot", 0, slot);
#ifdef DEBUG
show_sslots (k);
#endif
return (X_OK);
}
debug (DB_MSG, "RESEND opktinfo:", 0, 0);
debug (DB_LOG, " seq", 0, k->opktinfo[slot].seq);
debug (DB_CHR, " typ", 0, k->opktinfo[slot].typ);
debug (DB_LOG, " len", 0, k->opktinfo[slot].len);
debug (DB_LOG, " rtr", 0, k->opktinfo[slot].rtr);
debug (DB_LOG, " crc", 0, k->opktinfo[slot].crc);
debug (DB_LOG, " flg", 0, k->opktinfo[slot].flg);
k->opktlen = k->opktinfo[slot].len;
if (k->opktlen < 0 || k->opktlen >= P_BUFLEN)
{
debug (DB_LOG, "RESEND error opktlen", 0, k->opktlen);
return (X_ERROR);
}
// what about .seq, .typ, .rtr, .flg ?
if (!k->opktlen) /* Nothing to resend */
return (X_OK);
k->nresend++;
buf = k->opktinfo[slot].buf;
k->opktinfo[slot].rtr++;
if (k->opktinfo[slot].rtr > k->retry)
{
debug (DB_LOG, "RESEND error retries", 0, k->opktinfo[slot].rtr);
return (X_ERROR);
}
// debug (DB_PKT, ">PKT", &buf[1], k->opktlen);
ret = ((*(k->txd)) (k, buf, k->opktlen));
debug (DB_LOG, "RESEND txd ret", 0, ret);
return (ret);
}
int /* The kermit() function */
kermit (short fc, /* Function code */
struct k_data *k, /* The control struct */
int len, /* Length of packet in buf */
char *msg, /* Message for error packet */
struct k_response *r) /* Response struct */
{
int did_a_pkt = 0;
UCHAR *buf = 0;
short s_slot;
// int i, j, rc; /* Workers */
int i, rc; /* Workers */
int datalen = 0; /* Length of packet data field */
// int bctu; /* Block check type for this packet */
UCHAR *pdf = 0; /* Pointer to packet data field */
UCHAR *qdf = 0; /* Pointer to data to be checked */
UCHAR *s = 0; /* Worker string pointer */
// UCHAR c, t; /* Worker chars */
UCHAR rtyp = 0; /* Worker chars */
UCHAR c; /* Worker chars */
UCHAR pbc[4]; /* Copy of packet block check */
short rseq = 0; // actual received packet number
short rlen = 0;
short chklen; /* Length of packet block check */
unsigned int crc; /* 16-bit CRC */
int ok;
/* Mark each entry: */
debug (DB_LOG, "KERMIT ---------------------- version", VERSION, 0);
debug (DB_LOG, " fc", 0, fc);
debug (DB_LOG, " state", 0, k->state);
debug (DB_LOG, " zincnt", 0, (k->zincnt));
debug (DB_LOG, " k->wslots", 0, k->wslots);
test_rslots (k);
#ifdef DEBUG
show_sslots (k);
#endif
if (fc == K_INIT)
{ /* Initialize packet buffers etc */
k->version = (UCHAR *) VERSION; /* Version of this module */
r->filename[0] = '\0'; /* No filename yet. */
r->filedate[0] = '\0'; /* No filedate yet. */
r->filesize = 0L; /* No filesize yet. */
r->sofar = 0L; /* No bytes transferred yet */
r->sofar_rumor = 0L; /* No bytes transferred yet */
for (i = 0; i < P_WSLOTS; i++)
{ /* Packet info for each window slot */
free_rslot (k, i);
free_sslot (k, i);
}
for (i = 0; i < 64; i++)
{ /* Packet finder array */
k->r_pw[i] = -1; /* initialized to "no packets yet" */
k->s_pw[i] = -1; /* initialized to "no packets yet" */
}
/* Initialize the k_data structure */
k->sw_full = 0;
k->do_rxd = 1;
k->s_cnt = 0;
for (i = 0; i < 6; i++)
k->s_remain[i] = '\0';
k->state = R_WAIT; /* Beginning protocol state */
r->rstatus = R_WAIT;
k->what = W_RECV; /* Default action */
k->s_first = 1; /* Beginning of file */
k->r_soh = k->s_soh = SOH; /* Packet start */
k->r_eom = k->s_eom = CR; /* Packet end */
k->s_seq = k->r_seq = 0; /* Packet sequence number */
k->s_cnt = 0; /* Packet count */
k->s_type = k->r_type = 0; /* Packet type */
k->r_timo = P_R_TIMO; /* Timeout interval for me to use */
k->s_timo = P_S_TIMO; /* Timeout for other Kermit to use */
k->r_maxlen = k->p_maxlen; /* Maximum packet length */
k->s_maxlen = k->p_maxlen; /* Maximum packet length */
k->wslots = k->wslots_max; /* Current window slots */
k->zincnt = 0;
k->filename = (UCHAR *) 0;
/* Parity must be filled in by the caller */
k->retry = P_RETRY; /* Retransmission limit */
k->s_ctlq = k->r_ctlq = '#'; /* Control prefix */
k->ebq = 'Y'; /* 8th-bit prefix negotiation */
k->ebqflg = 0; /* 8th-bit prefixing flag */
k->rptq = '~'; /* Send repeat prefix */
k->rptflg = 0; /* Repeat counts negotiated */
k->s_rpt = 0; /* Current repeat count */
k->capas = 0 /* Capabilities */
| CAP_LP /* Long packets */
| CAP_AT /* Attribute packets */
;
if (k->wslots > 1)
k->capas |= CAP_SW; /* Sliding windows */
for (i = 0; i < P_WSLOTS; i++)
{
k->ipktinfo[i].buf = k->ipktbufs + i * P_BUFLEN;
k->ipktinfo[i].buf[0] = '\0';
k->ipktinfo[i].len = 0;
k->ipktinfo[i].seq = -1;
k->ipktinfo[i].typ = SP;
k->ipktinfo[i].dat = (UCHAR *) (0);
k->ipktinfo[i].crc = 0xFFFF;
}
for (i = 0; i < P_WSLOTS; i++)
{
k->opktinfo[i].buf = k->opktbuf + i * P_BUFLEN;
k->opktinfo[i].buf[0] = '\0';
k->opktinfo[i].len = 0;
k->opktinfo[i].seq = -1;
k->opktinfo[i].typ = SP;
k->opktinfo[i].dat = (UCHAR *) (0);
}
k->opktlen = 0;
/* This is the only way to initialize these tables -- no static data. */
k->crcta[0] = 0; /* CRC generation table A */
k->crcta[1] = 010201;
k->crcta[2] = 020402;
k->crcta[3] = 030603;
k->crcta[4] = 041004, k->crcta[5] = 051205;
k->crcta[6] = 061406;
k->crcta[7] = 071607;
k->crcta[8] = 0102010;
k->crcta[9] = 0112211;
k->crcta[10] = 0122412;
k->crcta[11] = 0132613;
k->crcta[12] = 0143014, k->crcta[13] = 0153215;
k->crcta[14] = 0163416;
k->crcta[15] = 0173617;
k->crctb[0] = 0; /* CRC table B */
k->crctb[1] = 010611;
k->crctb[2] = 021422;
k->crctb[3] = 031233;
k->crctb[4] = 043044;
k->crctb[5] = 053655;
k->crctb[6] = 062466;
k->crctb[7] = 072277;
k->crctb[8] = 0106110;
k->crctb[9] = 0116701;
k->crctb[10] = 0127532;
k->crctb[11] = 0137323;
k->crctb[12] = 0145154;
k->crctb[13] = 0155745;
k->crctb[14] = 0164576;
k->crctb[15] = 0174367;
// k->nspkt = 0;
// k->nsack = 0;
k->nsnak = 0;
k->nresend = 0;
// k->nepkt = 0;
// k->nbchk = 0;
// k->nshort = 0;
// k->nnaa = 0;
// k->ndiscard1 = 0;
// k->ndiscard2 = 0;
// k->ndiscard3 = 0;
return (X_OK);
}
else if (fc == K_GET)
{
/*
* Send R packet with filenames we want.
* Filenames cannot have spaces
* since names are separated by spaces.
*/
int i;
char *p, *q;
debug (DB_LOG, "function code == K_GET fc", 0, fc);
p = (char *) (k->obuf);
for (i = 0;; i++)
{
q = (char *) (k->filelist[i]);
if (q == 0)
break;
if (i > 0)
*p++ = ' ';
while (*q)
*p++ = *q++;
}
*p = 0;
debug (DB_LOG, "before encstr k->obuf", k->obuf, 0);
k->xdata = k->xdatabuf;
encstr (k->obuf, k, r); // Encode the name for transmission
debug (DB_LOG, "after encstr k->xdata", k->xdata, 0);
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
if (k->bcta3)
k->bct = 3;
else
k->bct = 1;
debug (DB_LOG, "K_GET sending R pkt s_seq", 0, k->s_seq);
if (spkt ('R', k->s_seq, -1, k->xdata, k) != X_OK)
{
debug (DB_LOG, "K_GET error spkt(R) failed fc", 0, fc);
return (X_ERROR); /* I/O error, quit. */
}
k->state = R_WAIT; /* All OK, switch states */
r->rstatus = R_WAIT;
k->what = W_GET;
return (X_OK);
}
else if (fc == K_SEND)
{
if (rpar (k, 'S') != X_OK) /* Send S packet with my parameters */
{
debug (DB_LOG, "K_SEND error rpar(S) failed fc", 0, fc);
return (X_ERROR); /* I/O error, quit. */
}
k->state = S_INIT; /* All OK, switch states */
r->rstatus = S_INIT;
k->what = W_SEND; /* Act like a sender */
return (X_OK);
}
else if (fc == K_STATUS)
{ /* Status report requested. */
debug (DB_LOG, "function code == K_STATUS fc", 0, fc);
return (X_STATUS); /* File name, date, size, if any. */
}
else if (fc == K_QUIT)
{ /* You told me to quit */
debug (DB_LOG, "function code == K_QUIT fc", 0, fc);
return (X_DONE); /* so I quit. */
}
else if (fc == K_SYNC)
{
debug (DB_LOG, "function code == K_SYNC fc", 0, fc);
epkt (msg, k);
return (X_OK);
}
else if (fc == K_ERROR)
{ /* Send an error packet... */
debug (DB_LOG, "K_ERROR error fc", 0, fc);
epkt (msg, k);
k->closef (k, 0, (k->state == S_DATA) ? 1 : 2); /* Close file */
return (X_DONE); /* and quit. */
}
else if (fc != K_RUN)
{ /* Anything else is an error. */
debug (DB_LOG, "not K_RUN error fc", 0, fc);
return (X_ERROR);
}
if (k->state == R_NONE) /* (probably unnecessary) */
return (X_OK);
/* If we're in the protocol, check to make sure we got a new packet */
debug (DB_MSG, "In the protocol", 0, 0);
if (k->do_rxd)
{ // we tried to read a packet in main loop
debug (DB_LOG, "DO_RXD len", 0, len); // in kermit call list
if (len < 4)
{ /* Packet obviously no good? */
int ret;
if (k->what == W_RECV) /* If receiving */
{
debug (DB_MSG, "DO_RXD len<4", 0, 0);
#ifdef USE_NAK_OLDEST_UNACKED
nak_oldest_unacked (k, -1);
#endif
if (k->state == R_FILE)
{
debug (DB_MSG, "calling rpar again", 0, 0);
rc = rpar (k, 'Y'); /* ACK again with my parameters */
}
return (X_OK);
}
else /* If W_SEND or W_GET */
{
debug (DB_MSG, "DO_RXD len<4: resending earliest", 0, 0);
ret = resend (k, -1); /* retransmit earliest packet in queue. */
return (ret);
}
}
/* Parse the packet */
if (k->what == W_RECV)
{ /* If we're sending ACKs */
switch (k->cancel)
{ /* Get cancellation code if any */
case 0:
s = (UCHAR *) 0;
break;
case 1:
s = (UCHAR *) "X";
break;
case 2:
s = (UCHAR *) "Z";
break;
}
}
pdf = k->ipktbuf;
qdf = pdf; /* Pointer to data to be checked */
rlen = xunchar (*pdf++); /* Length field */
rseq = xunchar (*pdf++); /* Received Sequence number */
rtyp = *pdf++; /* Type */
if (k->state == S_EOT &&
len == 4 && rseq == 0 && rtyp == 'N' && *pdf == 0x33)
{
debug (DB_MSG, "Got NAK for seq=0 after sending B-pkt", 0, 0);
debug (DB_MSG, " so assuming we are done.", 0, 0);
return (X_DONE); /* (or X_ERROR) */
}
// Really to use rseq the packet must be validated by CRC first.
if (rseq < 0 || rseq > 63)
{
debug (DB_LOG, "WARNING: TOSSING BAD PACKET: rseq", 0, rseq);
debug (DB_LOG, " rlen", 0, rlen);
debug (DB_CHR, " rtyp", 0, rtyp);
debug (DB_LOG, " what", 0, k->what);
debug (DB_PKT, " k->ipktbuf", k->ipktbuf, 0);
return (X_OK);
}
if ((k->what == W_RECV) && (rtyp == 'N' || rtyp == 'Y'))
{
/* Echo (it happens), ignore */
return (X_OK);
}
if (rlen == 0)
{ /* Length 0 means long packet */
c = pdf[2]; /* Get header checksum */
pdf[2] = '\0';
if (xunchar (c) != chk1 (pdf - 3, k))
{ /* Check it */
int ret;
debug (DB_MSG, "long pkt chk1 bad", 0, 0);
if (k->what == W_RECV)
{
#ifdef USE_NAK_OLDEST_UNACKED
debug (DB_MSG, "sending NAK for oldest unacked", 0, 0);
nak_oldest_unacked (k, -1); /* Send NAK */
#endif
return (X_OK);
}
else
{
debug (DB_MSG, "resending earliest", 0, 0);
ret = resend (k, -1);
debug (DB_LOG, "resend ret", 0, ret);
return (ret);
}
}
debug (DB_MSG, "HDR CHKSUM OK", 0, 0);
pdf[2] = c; /* Put checksum back */
if (k->bcta3)
k->bct = 3;
datalen = xunchar (pdf[0]) * 95 + xunchar (pdf[1]) - k->bct; /* Data length */
debug (DB_LOG, " long packet datalen", 0, datalen);
debug (DB_LOG, " rlen", 0, rlen);
debug (DB_LOG, " rseq", 0, rseq);
pdf += 3; /* Fix data pointer */
}
else
{ /* Regular packet */
if (k->bcta3)
k->bct = 3;
datalen = rlen - k->bct - 2; /* Data length */
debug (DB_LOG, "regular packet datalen", 0, datalen);
}
if (rtyp == 'S' || k->state == S_INIT)
{ /* S-packet was
* retransmitted? */
if (k->bcta3)
{
chklen = 3;
datalen = rlen - 5;
}
else
{
chklen = 1; /* Block check is always type 1 */
datalen = rlen - 3;
}
debug (DB_LOG, "S-packet datalen", 0, datalen);
}
else
{
if (k->bcta3)
k->bct = 3;
chklen = k->bct;
}
debug (DB_LOG, "DO_RXD state", 0, k->state);
debug (DB_MSG, " These are unverified: (before blk chk tested)", 0, 0);
debug (DB_LOG, " rlen", 0, rlen);
debug (DB_LOG, " rseq", 0, rseq);
debug (DB_CHR, " rtyp", 0, rtyp);
debug (DB_LOG, " bct", 0, k->bct);
debug (DB_LOG, " bcta3", 0, k->bcta3);
debug (DB_LOG, " datalen", 0, datalen);
debug (DB_LOG, " chklen", 0, chklen);
if (datalen < 0 || datalen + chklen + 1 >= P_BUFLEN)
{
debug (DB_MSG, "DO_RXD datalen out of bounds", 0, 0);
if (k->what == W_RECV)
{
#ifdef USE_NAK_OLDEST_UNACKED
debug (DB_LOG, " NAK oldest or rseq", 0, rseq);
nak_oldest_unacked (k, rseq);
#endif
}
else
{
debug (DB_MSG, " resend earliest", 0, 0);
resend (k, -1);
}
return (X_OK);
}
for (i = 0; i < chklen; i++) /* Copy the block check */
pbc[i] = pdf[datalen + i];
pbc[i] = '\0'; /* Null-terminate block check string */
pdf[datalen] = '\0'; /* and the packet DATA field. */
switch (chklen)
{ /* Check the block check */
case 1: /* Type 1, 6-bit checksum */
ok = (xunchar (*pbc) == chk1 (qdf, k));
#ifdef DEBUG
if (ok && xerror ())
ok = 0;
#endif /* DEBUG */
if (!ok)
{
debug (DB_CHR, "6-bit checksum ERROR rtyp", 0, rtyp);
debug (DB_LOG, " rseq", 0, rseq);
debug (DB_PKT, " k->ipktbuf", k->ipktbuf, 0);
if (k->what == W_RECV)
{
#ifdef USE_NAK_OLDEST_UNACKED
debug (DB_LOG, " NAK oldest or rseq", 0, rseq);
nak_oldest_unacked (k, rseq);
#endif
}
else
{
debug (DB_MSG, " resend earliest", 0, 0);
resend (k, -1);
}
return (X_OK);
}
break;
case 2: /* Type 2, 12-bit checksum */
i = xunchar (*pbc) << 6 | xunchar (pbc[1]);
ok = (i == chk2 (qdf, k));
#ifdef DEBUG
if (ok && xerror ())
ok = 0;
#endif /* DEBUG */
if (!ok)
{ /* No match */
debug (DB_CHR, "12-bit checksum ERROR rtyp", 0, rtyp);
debug (DB_LOG, " rseq", 0, rseq);
debug (DB_PKT, " k->ipktbuf", k->ipktbuf, 0);
if (rtyp == 'E')
{ /* Allow E packets to have type 1 */
int j;
j = datalen;
pdf[j++] = pbc[0];
pdf[j] = '\0';
if (xunchar (pbc[1]) == chk1 (qdf, k))
break;
else
pdf[--j] = '\0';
}
if (k->what == W_RECV)
{
#ifdef USE_NAK_OLDEST_UNACKED
debug (DB_LOG, " NAK oldest or rseq", 0, rseq);
nak_oldest_unacked (k, rseq);
#endif
}
else
{
debug (DB_MSG, " resend earliest", 0, 0);
resend (k, -1);
}
return (X_OK);
}
break;
case 3: /* Type 3, 16-bit CRC */
crc = (xunchar (pbc[0]) << 12)
| (xunchar (pbc[1]) << 6) | (xunchar (pbc[2]));
ok = (crc == chk3 (qdf, k));
#ifdef DEBUG
if (ok && xerror ())
{
ok = 0;
debug (DB_MSG, "INPUT CRC ERROR INJECTED", 0, 0);
}
#endif /* DEBUG */
if (!ok)
{
debug (DB_MSG, "Packet blk chk3 bad", 0, 0);
debug (DB_LOG, " rseq", 0, rseq);
if (rtyp == 'E')
{ /* Allow E packets to have type 1 */
int j;
j = datalen;
pdf[j++] = pbc[0];
pdf[j++] = pbc[1];
pdf[j] = '\0';
if (xunchar (pbc[2]) == chk1 (qdf, k))
break;
else
{
j -= 2;
pdf[j] = '\0';
}
}
if (k->what == W_RECV)
{
#ifdef USE_NAK_OLDEST_UNACKED
debug (DB_LOG, " NAK oldest or rseq", 0, rseq);
nak_oldest_unacked (k, rseq);
#endif
}
else
{
debug (DB_MSG, " resend earliest", 0, 0);
resend (k, -1);
}
return (X_OK);
}
}
// now the packet has a good block check
debug (DB_MSG, "Packet blk chk good", 0, 0);
debug (DB_CHR, " rtyp", 0, rtyp);
debug (DB_LOG, " rseq", 0, rseq);
debug (DB_LOG, " k->r_seq", 0, k->r_seq);
debug (DB_LOG, " k->state", 0, k->state);
debug (DB_LOG, " chklen", 0, chklen);
debug (DB_LOG, " datalen", 0, datalen);
if (rtyp == 'E') /* (AND CLOSE FILES?) */
{
debug (DB_MSG, "error msg received from other kermit", 0, 0);
debug (DB_PKT, "EPKT", pdf, 0);
return (X_ERROR);
}
if (k->what == W_SEND) /* Sending, check for ACK */
{
if (rtyp != 'Y')
{
int ret;
debug (DB_CHR, "rtyp not Y, rtyp", 0, rtyp);
if (k->state == S_DATA &&
rtyp == 'N' && rseq == ((k->r_seq + 1) & 63))
{
debug (DB_MSG,
"FYI got NAK for packet just after last one sent", 0,
0);
#ifdef DEBUG
show_sslots (k);
#endif
# if 0
debug (DB_MSG, "W_SEND Freeing all send slots", 0, 0);
for (i = 0; i < k->wslots; i++)
free_sslot (k, i);
k->sw_full = 0;
# endif
}
if (nused_sslots (k) == 0)
{
debug (DB_LOG, "sending requested rseq", 0, rseq);
nxtpkt (k);
if (k->s_seq != rseq)
{
debug (DB_LOG, "error confused s_seq", 0, k->s_seq);
debug (DB_LOG, " rseq", 0, rseq);
epkt ("EKSW W_SEND confused", k);
// exit (1);
return (X_ERROR);
}
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
rc = sdata (k, r); /* Send next data packet */
if (rc == X_ERROR)
return (rc);
if (rc == 0)
{ /* If there was no data to send */
k->closef (k, 0, 1); /* Close input file */
k->state = S_EOF; /* And wait for ACK */
r->rstatus = S_EOF;
k->s_seq--;
if (k->s_seq < 0)
k->s_seq += 64;
free_sslot (k, s_slot);
} /* Otherwise stay in data state */
k->r_seq = k->s_seq; /* Sequence number to wait for */
return (X_OK);
}
else
{
debug (DB_LOG, "6: resending rseq", 0, rseq);
// resend will send rseq if it's in the send table
// otherwise it will resend earliest unacked packet
ret = resend (k, rseq);
debug (DB_LOG, "ret", 0, ret);
return (ret);
}
}
// we're sending, received an ACK and packet block check OK
s_slot = k->s_pw[rseq];
if (s_slot < 0 || s_slot >= k->wslots)
{
debug (DB_LOG, "ignoring ack -- not in table rseq", 0, rseq);
#ifdef DEBUG
show_sslots (k);
#endif
return (X_OK);
}
// set ACK'd flag for that send slot
k->opktinfo[s_slot].flg = 1;
// remove earliest and subsequent
free_sslot_easca (k);
#ifdef DEBUG
// check that send table sequence numbers are in order
chk_sseq_nos (k);
#endif
if (k->state == S_DATA)
{ /* ACK to Data packet? */
if (k->cancel || /* Cancellation requested by caller? */
*pdf == 'X' || *pdf == 'Z')
{ /* Or by receiver? */
k->closef (k, 0, 1); /* Close input file */
nxtpkt (k); /* Next packet sequence number */
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "Cancellation: sending Z pkt s_seq", 0,
k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k);
debug (DB_LOG, " rc", 0, rc);
if (rc != X_OK)
{
return (rc);
}
if (*pdf == 'Z' || k->cancel == I_GROUP)
{ /* Cancel Group? */
debug (DB_MSG, "Group Cancel (Send)", 0, 0);
while (*(k->filelist))
{ /* Go to end of file list */
debug (DB_LOG, "Skip", *(k->filelist), 0);
(k->filelist)++;
}
}
k->state = S_EOF; /* Wait for ACK to EOF */
r->rstatus = S_EOF;
k->r_seq = k->s_seq; /* Sequence number of packet we want */
return (X_OK);
}
}
r->sofar = r->sofar_rumor;
debug (DB_LOG, "end of W_SEND rseq", 0, rseq);
debug (DB_LOG, " k->r_seq", 0, k->r_seq);
debug (DB_LOG, " k->state", 0, k->state);
debug (DB_CHR, " rtyp", 0, rtyp);
} // if (k->what == W_SEND)
} // if(k->do_rxd)
switch (k->state)
{ /* Kermit protocol state switcher */
case S_INIT: /* Got other Kermit's parameters */
case S_EOF: /* got ack to Z packet */
if (k->state == S_INIT)
{ /* Got ACK to S packet? */
debug (DB_MSG, "S_INIT", 0, 0);
spar (k, pdf, datalen); /* Set negotiated parameters */
debug (DB_CHR, "Parity", 0, k->parity);
debug (DB_LOG, "Ebqflg", 0, k->ebqflg);
debug (DB_CHR, "Ebq", 0, k->ebq);
}
else
{
debug (DB_LOG, "S_EOF rseq", 0, rseq);
# if 0 // don't use k->r_seq to switch states -- use nused_sslots()
debug (DB_LOG, "S_EOF k->r_seq", 0, k->r_seq);
if (rseq != k->r_seq) // keep reading until last packet ACKed
return (X_OK);
# endif
if ((i = nused_sslots (k)) > 0)
{
debug (DB_LOG, "keep reading until all sslots free. nused", 0,
i);
#ifdef DEBUG
show_sslots (k);
#endif
return (X_OK);
}
}
nxtpkt (k); /* Get next packet number etc */
k->filename = *(k->filelist); /* Get next filename */
if (k->filename)
{ /* If there is one */
int i;
for (i = 0; i < FN_MAX; i++)
{ /* Copy name to result struct
*/
r->filename[i] = k->filename[i];
if (!(r->filename[i]))
break;
}
(k->filelist)++;
debug (DB_LOG, "Filename", k->filename, 0);
if ((rc = (k->openf) (k, k->filename, 1)) != X_OK) /* Try to open */
{
debug (DB_LOG, "k->openf failed rc", 0, rc);
return (rc);
}
encstr (k->filename, k, r); /* Encode the name for transmission */
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "sending F pkt k->s_seq", 0, k->s_seq);
debug (DB_LOG, "sending F pkt s_slot", 0, s_slot);
k->sw_full = 0;
if ((rc = spkt ('F', k->s_seq, -1, k->xdata, k)) != X_OK)
{
return (rc); /* Send F packet */
}
r->sofar = 0L;
r->sofar_rumor = 0L;
k->state = S_FILE; /* Wait for ACK */
r->rstatus = S_FILE;
}
else
{ /* No more files - we're done */
# if 0 // this was a hack but should not be needed
int nused = nused_sslots (k);
debug (DB_LOG, "want to send B pkt nused", 0, nused);
if (nused == k->wslots)
{
short rm_sseq;
short rm_sslot;
rm_sseq = earliest_sseq (k);
if (rm_sseq < 0 || rm_sseq >= k->wslots)
{
debug (DB_LOG, "want to send B pkt error rm_sseq", 0, rm_sseq);
return (X_ERROR);
}
rm_sslot = k->s_pw[rm_sseq];
if (rm_sslot < 0 || rm_sslot >= k->wslots)
{
debug (DB_LOG, "want to send B pkt error rm_sslot", 0,
rm_sslot);
debug (DB_LOG, " rm_sseq", 0, rm_sseq);
return (X_ERROR);
}
free_sslot (k, rm_sslot);
}
# endif
// all sslots are supposed to be free at this point
buf = get_sslot (k, &s_slot); // get a new send slot
if (s_slot < 0 || s_slot >= k->wslots)
{
debug (DB_LOG, "want to send B pkt error s_slot", 0, s_slot);
debug (DB_LOG, " k->s_seq", 0, k->s_seq);
return (X_ERROR);
}
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "sending B pkt k->s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
if ((rc = spkt ('B', k->s_seq, 0, (UCHAR *) 0, k)) != X_OK)
{
return (rc); /* Send EOT packet */
}
k->state = S_EOT; /* Wait for ACK to B packet, end of transmission */
r->rstatus = S_EOT;
}
k->r_seq = k->s_seq; /* Sequence number of packet we want */
return (X_OK); /* Return to control program */
case S_FILE: /* Got ACK to F packet */
nxtpkt (k); /* Get next packet number etc */
if (k->capas & CAP_AT)
{ /* A-packets negotiated? */
if ((rc = sattr (k, r)) != X_OK) /* Yes, send Attribute packet */
{
debug (DB_LOG, "SATTR failed rc", 0, rc);
return (rc);
}
k->state = S_ATTR; /* And wait for its ACK */
r->rstatus = S_ATTR;
did_a_pkt = 1;
}
else
{
did_a_pkt = 0;
}
if (did_a_pkt == 0)
{
/* No A packets - send first data */
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "sending first D k->s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
rc = sdata (k, r); /* Send next data packet */
if (rc == X_ERROR)
return (rc);
if (rc == 0)
{
/* File is empty so send EOF packet */
buf = get_sslot (k, &s_slot); // get a new send slot
k->s_pw[k->s_seq] = s_slot;
debug (DB_LOG, "empty file: sending Z pkt k->s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
if ((rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k)) != X_OK)
{
return (rc);
}
k->closef (k, 0, 1); /* Close input file */
k->state = S_EOF; /* Wait for ACK to EOF */
r->rstatus = S_EOF;
}
else
{ /* Sent some data */
k->state = S_DATA; /* Wait for ACK to first data */
r->rstatus = S_DATA;
}
} // if(did_a_pkt==0)
k->r_seq = k->s_seq; /* Sequence number to wait for */
return (X_OK);
case S_ATTR: /* Got ACK to A packet */
case S_DATA: /* Got ACK to D packet */
if (k->state == S_ATTR)
{
/*
* CHECK ATTRIBUTE RESPONSE
*/
/*
* IF REJECTED do the right thing...
* Left as an exersise for the reader...
*/
k->state = S_DATA;
r->rstatus = S_DATA;
}
buf = get_sslot (k, &s_slot); // get a new send slot
if (s_slot < 0)
{
debug (DB_LOG, "window full k->state", 0, k->state);
k->sw_full = 1;
return (X_OK);
}
nxtpkt (k); /* Get next packet number */
k->s_pw[k->s_seq] = s_slot;
k->sw_full = 0;
debug (DB_LOG, "S_DATA sending k->s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
rc = sdata (k, r); /* Send first or next data packet */
debug (DB_LOG, "S_DATA sdata rc", 0, rc);
debug (DB_LOG, " k->s_seq", 0, k->s_seq);
if (rc == X_ERROR)
return (rc);
if (rc == 0)
{ /* If there was no data to send */
k->closef (k, 0, 1); /* Close input file */
debug (DB_LOG, "NUSED_SSLOTS", 0, nused_sslots (k));
k->state = S_EOF; /* And wait for ACK to Z pkt */
r->rstatus = S_EOF;
// sdata() above did not send D pkt so send Z pkt in its place
debug (DB_LOG, "EOF so sending Z pkt k->s_seq", 0, k->s_seq);
debug (DB_LOG, " s_slot", 0, s_slot);
if ((rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k)) != X_OK)
return (rc);
} /* Otherwise stay in data state */
k->r_seq = k->s_seq; /* Sequence number to wait for */
return (X_OK);
case S_EOT: /* Get ACK to EOT packet */
debug (DB_MSG, "S_EOT X_DONE", 0, 0);
return (X_DONE); /* (or X_ERROR) */
case R_WAIT: /* Waiting for the S packet */
debug (DB_CHR, "R_WAIT rtyp", 0, rtyp);
debug (DB_LOG, " rseq", 0, rseq);
debug (DB_LOG, " what", 0, k->what);
if (rtyp == 'S')
{ /* Got it */
spar (k, pdf, datalen); /* Set parameters from it */
debug (DB_MSG, "R_WAIT rtyp==S after spar", 0, 0);
debug (DB_CHR, " Parity", 0, k->parity);
debug (DB_LOG, " Ebqflg", 0, k->ebqflg);
debug (DB_CHR, " Ebq", 0, k->ebq);
rc = rpar (k, 'Y'); /* ACK with my parameters */
debug (DB_LOG, "R_WAIT rtyp==S after rpar rc", 0, rc);
debug (DB_LOG, " k->capas & CAP_LP", 0, k->capas & CAP_LP);
debug (DB_LOG, " k->capas & CAP_SW", 0, k->capas & CAP_SW);
debug (DB_LOG, " k->capas & CAP_AT", 0, k->capas & CAP_AT);
debug (DB_LOG, " k->capas & CAP_RS", 0, k->capas & CAP_RS);
debug (DB_LOG, " k->capas & CAP_LS", 0, k->capas & CAP_LS);
debug (DB_CHR, " k->ebq ", 0, k->ebq);
debug (DB_LOG, " k->ebqflg ", 0, k->ebqflg);
debug (DB_LOG, " k->parity ", 0, k->parity);
debug (DB_LOG, " k->s_eom ", 0, k->s_eom);
debug (DB_LOG, " k->r_timo ", 0, k->r_timo);
debug (DB_LOG, " k->s_timo ", 0, k->s_timo);
debug (DB_CHR, " k->r_ctlq ", 0, k->r_ctlq);
debug (DB_CHR, " k->s_ctlq ", 0, k->s_ctlq);
debug (DB_CHR, " k->rptq ", 0, k->rptq);
debug (DB_LOG, " k->rptflg ", 0, k->rptflg);
debug (DB_LOG, " k->bct ", 0, k->bct);
debug (DB_LOG, " k->bcta3 ", 0, k->bcta3);
debug (DB_LOG, " k->r_maxlen ", 0, k->r_maxlen);
debug (DB_LOG, " k->s_maxlen ", 0, k->s_maxlen);
debug (DB_LOG, " k->wslots ", 0, k->wslots);
debug (DB_LOG, " k->binary ", 0, k->binary);
debug (DB_LOG, " k->retry ", 0, k->retry);
if (rc != X_OK)
{
debug (DB_MSG, "R_WAIT error rpar(Y) failed", 0, 0);
return (X_ERROR); /* I/O error, quit. */
}
k->state = R_FILE; /* All OK, switch states */
r->rstatus = R_FILE;
k->what = W_RECV;
}
else if (k->what == W_GET)
{
debug (DB_MSG, " what==W_GET so resending R-packet", 0, 0);
rc = resend (k, 0);
}
else
{
debug (DB_MSG, " unexpected packet so send NAK for seq 0", 0, 0);
rc = nak (k, 0, -1);
}
if (rc != X_OK)
debug (DB_LOG, "R_WAIT rc not X_OK: rc", 0, rc);
return (rc);
case R_FILE: /* Want an F or B packet, may get Z and S pkt */
debug (DB_CHR, "R_FILE rtyp", 0, rtyp);
if (rtyp == 'F')
{ /* File name */
if ((rc = decode (k, r, 0, pdf, -1)) == X_OK) /* Decode and save */
k->state = R_ATTR; /* Switch to next state */
r->rstatus = k->state;
debug (DB_LOG, "R_FILE decode rc", 0, rc);
debug (DB_LOG, "R_FILE FILENAME", r->filename, 0);
if (rc == X_OK)
{ /* All OK so far */
r->filedate[0] = '\0'; /* No file date yet */
r->filesize = 0L; /* Or file size */
r->sofar = 0L; /* Or bytes transferred yet */
r->sofar_rumor = 0L; /* Or bytes transferred yet */
rc = ack (k, rseq, r->filename); /* so ACK the F packet */
}
else
{
epkt ("eksw Filename error", k); /* Error decoding filename */
return (rc);
}
}
else if (rtyp == 'B')
{ /* Break, end of transaction */
// Extra ACKs to insure they heard the ACK to their B-packet.
// But they send extra NAKs in response.
// ack (k, rseq, (UCHAR *) 0);
// ack (k, rseq, (UCHAR *) 0);
rc = (ack (k, rseq, (UCHAR *) 0) == X_OK) ? X_DONE : X_ERROR;
}
else if (rtyp == 'Z')
{ /* End of file again */
rc = ack (k, rseq, (UCHAR *) 0);
}
else if (rtyp == 'S')
{ /* got S pkt again */
spar (k, pdf, datalen); /* Set parameters from it */
debug (DB_MSG, "R_FILE rtyp==S again", 0, 0);
debug (DB_CHR, " Parity", 0, k->parity);
debug (DB_LOG, " Ebqflg", 0, k->ebqflg);
debug (DB_CHR, " Ebq", 0, k->ebq);
rc = rpar (k, 'Y'); /* ACK with my parameters */
debug (DB_LOG, "R_FILE after rpar rc", 0, rc);
if (rc != X_OK)
{
debug (DB_MSG, "R_FILE error rpar(Y) failed", 0, 0);
return (X_ERROR); /* I/O error, quit. */
}
k->state = R_FILE; /* All OK, switch states */
r->rstatus = R_FILE;
k->what = W_RECV;
}
else
{
rc = X_ERROR;
}
debug (DB_LOG, "rc", 0, rc);
return (rc);
case R_ATTR: /* Want A, D, or Z packet */
debug (DB_CHR, "R_ATTR rtyp", 0, rtyp);
if (rtyp == 'A')
{ /* Attribute packet */
int x;
x = gattr (k, pdf, r); /* Read the attributes */
if (x > -1)
k->binary = x;
ack (k, rseq, (UCHAR *) "Y"); /* Always accept the file */
return (X_OK);
}
else if (rtyp == 'D')
{ /* First data packet */
k->obufpos = 0; /* Initialize output buffer */
k->filename = r->filename;
r->sofar = 0L;
r->sofar_rumor = 0L;
if ((rc = (*(k->openf)) (k, r->filename, 2)) == X_OK)
{
k->state = R_DATA; /* Switch to Data state */
r->rstatus = k->state;
rc = handle_good_rpkt (k, r, rseq, pdf);
}
else
{
debug (DB_MSG, "eksw cannot open output file", 0, 0);
epkt ("eksw cannot open output file", k);
return (rc);
}
if (rc == X_OK)
{
}
else
{
debug (DB_MSG, "eksw Error writing data to file", 0, 0);
epkt ("eksw Error writing data to file", k);
}
return (rc);
}
else if (rtyp == 'Z')
{ /* Empty file */
debug (DB_LOG, "R_ATTR empty file", r->filename, 0);
k->obufpos = 0; /* Initialize output buffer */
k->filename = r->filename;
r->sofar = 0L; /* Open and close the file */
r->sofar_rumor = 0L; /* Open and close the file */
if ((rc = (*(k->openf)) (k, r->filename, 2)) == X_OK)
{
if (((rc = (*(k->closef)) (k, *pdf, 2)) == X_OK))
{
k->state = R_FILE;
rc = ack (k, rseq, s);
}
else
{
debug (DB_MSG, "eksw Error closing empty file", 0, 0);
epkt ("eksw Error closing empty file", k);
return (rc);
}
}
else
{
debug (DB_LOG, "eksw File refused or cannot be opened rc", 0, rc);
epkt ("eksw File refused or cannot be opened", k);
return (rc);
}
r->rstatus = k->state;
return (X_OK);
}
else if (rtyp == 'F') // received F pkt again
{
ack (k, rseq, (UCHAR *) 0);
return (X_OK);
}
else
{
debug (DB_CHR, "R_ATTR error 3 unexpected packet rtyp", 0, rtyp);
epkt ("eksw R_ATTR error 3 unexpected packet type", k);
// exit (1);
return (X_ERROR);
}
// break;
case R_DATA: /* Want a D or Z packet */
debug (DB_CHR, "R_DATA rtyp", 0, rtyp);
debug (DB_LOG, "R_DATA rseq", 0, rseq);
if (rtyp == 'D')
{ /* Data */
rc = handle_good_rpkt (k, r, rseq, pdf);
debug (DB_LOG, "R_DATA hgr rc", 0, rc);
}
else if (rtyp == 'Z')
{ /* End of file */
debug (DB_LOG, "R_DATA Z pkt obufpos", 0, k->obufpos);
flush_to_file (k, r);
if (((rc = (*(k->closef)) (k, *pdf, 2)) == X_OK) && (rc == X_OK))
k->state = R_FILE;
debug (DB_LOG, "R_DATA closef rc", 0, rc);
r->rstatus = k->state;
}
else
{
debug (DB_CHR, "R_DATA error 4 unexpected packet rtyp", 0, rtyp);
epkt ("eksw error 4 unexpected packet type", k);
// exit (1);
return (X_ERROR);
}
if (rc == X_OK)
{
if (rtyp == 'Z')
{
rc = ack (k, rseq, s); // usual ACK to D or Z packet
}
}
else
{
debug (DB_CHR, "Error 12: rc != X_OK rtyp", 0, rtyp);
epkt (rtyp == 'Z' ? "eksw Can't close file" : "eksw hgr failed", k);
}
return (rc);
case R_ERROR: /* Canceled from above */
debug (DB_LOG, "R_ERROR error so sending E pkt k->state", 0, k->state);
epkt (msg, k);
return (X_ERROR);
default:
debug (DB_LOG, "default error so sending E pkt k->state", 0, k->state);
epkt (msg, k);
return (X_ERROR);
}
// not supposed to get here
// debug (DB_LOG, "Kermit logic error k->state", 0, k->state);
// exit (1);
// return (X_ERROR); /* make compiler happy */
}