mirror of
https://github.com/Stichting-MINIX-Research-Foundation/netbsd.git
synced 2025-09-09 23:27:35 -04:00
1620 lines
52 KiB
C
1620 lines
52 KiB
C
#define KERMIT_C
|
|
/*
|
|
Embedded Kermit protocol module
|
|
Version: 1.7
|
|
Most Recent Update: Mon Jun 6 15:36:26 2011
|
|
|
|
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 can't 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.
|
|
|
|
Author: Frank da Cruz.
|
|
As of version 1.6 of 30 March 2011, E-Kermit is Open Source software under
|
|
the Revised 3-Clause BSD license which follows. E-Kermit 1.6 is identical
|
|
to version 1.51 except for the new license.
|
|
|
|
Author: Frank da Cruz.
|
|
|
|
Copyright (C) 1995, 2011,
|
|
Trustees of Columbia University in the City of New York.
|
|
All rights reserved.
|
|
|
|
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.
|
|
*/
|
|
#include "cdefs.h" /* C language defs for all modules */
|
|
#include "debug.h" /* Debugging */
|
|
#include "kermit.h" /* Kermit protocol definitions */
|
|
|
|
#define zgetc() \
|
|
((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):(*(k->readf))(k)
|
|
|
|
/* 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 *);
|
|
int STATIC spkt(char, short, int, UCHAR *, struct k_data *);
|
|
int STATIC ack(struct k_data *, short, UCHAR * text);
|
|
int STATIC nak(struct k_data *, short, short);
|
|
int STATIC chk1(UCHAR *, struct k_data *);
|
|
STATIC USHORT chk2(UCHAR *, struct k_data *);
|
|
#ifdef F_CRC
|
|
STATIC USHORT chk3(UCHAR *, struct k_data *);
|
|
#endif /* F_CRC */
|
|
void STATIC spar(struct k_data *, UCHAR *, int);
|
|
int STATIC rpar(struct k_data *, char);
|
|
int STATIC decode(struct k_data *, struct k_response *, short, UCHAR *);
|
|
#ifdef F_AT
|
|
int STATIC gattr(struct k_data *, UCHAR *, struct k_response *);
|
|
int STATIC sattr(struct k_data *, struct k_response *);
|
|
#endif /* F_AT */
|
|
#ifndef RECVONLY
|
|
int STATIC sdata(struct k_data *, struct k_response *);
|
|
#endif /* RECVONLY */
|
|
void STATIC epkt(char *, struct k_data *);
|
|
int STATIC getpkt(struct k_data *, struct k_response *);
|
|
int STATIC encstr(UCHAR *, struct k_data *, struct k_response *);
|
|
void STATIC decstr(UCHAR *, struct k_data *, struct k_response *);
|
|
void STATIC encode(int, int, struct k_data *);
|
|
int STATIC nxtpkt(struct k_data *);
|
|
int STATIC resend(struct k_data *);
|
|
#ifdef DEBUG
|
|
int xerror(void);
|
|
#endif /* DEBUG */
|
|
|
|
int /* The kermit() function */
|
|
kermit(short f, /* Function code */
|
|
struct k_data *k, /* The control struct */
|
|
short r_slot, /* Received packet slot number */
|
|
int len, /* Length of packet in slot */
|
|
char *msg, /* Message for error packet */
|
|
struct k_response *r) { /* Response struct */
|
|
|
|
int i, j, rc; /* Workers */
|
|
int datalen; /* Length of packet data field */
|
|
UCHAR *p; /* Pointer to packet data field */
|
|
UCHAR *q; /* Pointer to data to be checked */
|
|
UCHAR *s; /* Worker string pointer */
|
|
UCHAR c, t; /* Worker chars */
|
|
UCHAR pbc[4]; /* Copy of packet block check */
|
|
short seq, prev; /* Copies of sequence numbers */
|
|
short chklen; /* Length of packet block check */
|
|
#ifdef F_CRC
|
|
unsigned int crc; /* 16-bit CRC */
|
|
#endif /* F_CRC */
|
|
int ok;
|
|
|
|
debug(DB_MSG,"----------",0,0); /* Marks each entry */
|
|
debug(DB_LOG,"f",0,f);
|
|
debug(DB_LOG,"state",0,k->state);
|
|
debug(DB_LOG,"zincnt",0,(k->zincnt));
|
|
|
|
if (f == 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 */
|
|
|
|
for (i = 0; i < P_WSLOTS; i++) { /* Packet info for each window slot */
|
|
freerslot(k,i);
|
|
freesslot(k,i);
|
|
}
|
|
#ifdef F_TSW
|
|
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" */
|
|
}
|
|
#endif /* F_TSW */
|
|
|
|
/* Initialize the k_data structure */
|
|
|
|
for (i = 0; i < 6; i++)
|
|
k->s_remain[i] = '\0';
|
|
|
|
k->state = R_WAIT; /* Beginning protocol state */
|
|
r->status = 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_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 = P_PKTLEN; /* Maximum packet length */
|
|
k->s_maxlen = P_PKTLEN; /* Maximum packet length */
|
|
k->window = P_WSLOTS; /* Maximum window slots */
|
|
k->wslots = 1; /* Current window slots */
|
|
k->zincnt = 0;
|
|
k->dummy = 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 */
|
|
#ifdef F_LP
|
|
| CAP_LP /* Long packets */
|
|
#endif /* F_LP */
|
|
#ifdef F_SW
|
|
| CAP_SW /* Sliding windows */
|
|
#endif /* F_SW */
|
|
#ifdef F_AT
|
|
| CAP_AT /* Attribute packets */
|
|
#endif /* F_AT */
|
|
;
|
|
|
|
k->opktbuf[0] = '\0'; /* No packets sent yet. */
|
|
k->opktlen = 0;
|
|
|
|
#ifdef F_CRC
|
|
/* 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;
|
|
#endif /* F_CRC */
|
|
|
|
return(X_OK);
|
|
|
|
#ifndef RECVONLY
|
|
} else if (f == K_SEND) {
|
|
if (rpar(k,'S') != X_OK) /* Send S packet with my parameters */
|
|
return(X_ERROR); /* I/O error, quit. */
|
|
k->state = S_INIT; /* All OK, switch states */
|
|
r->status = S_INIT;
|
|
k->what = W_SEND; /* Act like a sender */
|
|
return(X_OK);
|
|
#endif /* RECVONLY */
|
|
|
|
} else if (f == K_STATUS) { /* Status report requested. */
|
|
return(X_STATUS); /* File name, date, size, if any. */
|
|
|
|
} else if (f == K_QUIT) { /* You told me to quit */
|
|
return(X_DONE); /* so I quit. */
|
|
|
|
} else if (f == K_ERROR) { /* Send an error packet... */
|
|
epkt(msg,k);
|
|
k->closef(k,0,(k->state == S_DATA) ? 1 : 2); /* Close file */
|
|
return(X_DONE); /* and quit. */
|
|
|
|
} else if (f != K_RUN) { /* Anything else is an error. */
|
|
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_LOG,"r_slot",0,r_slot);
|
|
debug(DB_LOG,"len",0,len);
|
|
|
|
if (r_slot < 0) /* We should have a slot here */
|
|
return(K_ERROR);
|
|
else
|
|
k->ipktinfo[r_slot].len = len; /* Copy packet length to ipktinfo. */
|
|
|
|
if (len < 4) { /* Packet obviously no good? */
|
|
#ifdef RECVONLY
|
|
return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */
|
|
#else
|
|
if (k->what == W_RECV) /* If receiving */
|
|
return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */
|
|
else /* If sending */
|
|
return(resend(k)); /* retransmit last packet. */
|
|
#endif /* RECVONLY */
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
p = &(k->ipktbuf[0][r_slot]); /* Point to it */
|
|
|
|
q = p; /* Pointer to data to be checked */
|
|
k->ipktinfo[r_slot].len = xunchar(*p++); /* Length field */
|
|
seq = k->ipktinfo[r_slot].seq = xunchar(*p++); /* Sequence number */
|
|
t = k->ipktinfo[r_slot].typ = *p++; /* Type */
|
|
|
|
if (
|
|
#ifndef RECVONLY
|
|
(k->what == W_RECV) && /* Echo (it happens), ignore */
|
|
#endif /* RECVONLY */
|
|
(t == 'N' || t == 'Y')) {
|
|
freerslot(k,r_slot);
|
|
return(X_OK);
|
|
}
|
|
k->ipktinfo[r_slot].dat = p; /* Data field, maybe */
|
|
#ifdef F_LP
|
|
if (k->ipktinfo[r_slot].len == 0) { /* Length 0 means long packet */
|
|
c = p[2]; /* Get header checksum */
|
|
p[2] = '\0';
|
|
if (xunchar(c) != chk1(p-3,k)) { /* Check it */
|
|
freerslot(k,r_slot); /* Bad */
|
|
debug(DB_MSG,"HDR CHKSUM BAD",0,0);
|
|
#ifdef RECVONLY
|
|
return(nak(k,k->r_seq,r_slot)); /* Send NAK */
|
|
#else
|
|
if (k->what == W_RECV)
|
|
return(nak(k,k->r_seq,r_slot)); /* Send NAK */
|
|
else
|
|
return(resend(k));
|
|
#endif /* RECVONLY */
|
|
}
|
|
debug(DB_MSG,"HDR CHKSUM OK",0,0);
|
|
p[2] = c; /* Put checksum back */
|
|
/* Data length */
|
|
datalen = xunchar(p[0])*95 + xunchar(p[1]) - ((k->bctf) ? 3 : k->bct);
|
|
p += 3; /* Fix data pointer */
|
|
k->ipktinfo[r_slot].dat = p; /* Permanent record of data pointer */
|
|
} else { /* Regular packet */
|
|
#endif /* F_LP */
|
|
datalen = k->ipktinfo[r_slot].len - k->bct - 2; /* Data length */
|
|
#ifdef F_LP
|
|
}
|
|
#endif /* F_LP */
|
|
#ifdef F_CRC
|
|
if (k->bctf) { /* FORCE 3 */
|
|
chklen = 3;
|
|
} else {
|
|
if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */
|
|
if (q[10] == '5') { /* Block check type requested is 5 */
|
|
k->bctf = 1; /* FORCE 3 */
|
|
chklen = 3;
|
|
}
|
|
chklen = 1; /* Block check is always type 1 */
|
|
datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
|
|
} else {
|
|
chklen = k->bct;
|
|
}
|
|
}
|
|
#else
|
|
chklen = 1; /* Block check is always type 1 */
|
|
datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
|
|
#endif /* F_CRC */
|
|
debug(DB_LOG,"bct",0,(k->bct));
|
|
debug(DB_LOG,"datalen",0,datalen);
|
|
debug(DB_LOG,"chkalen",0,chklen);
|
|
|
|
#ifdef F_CRC
|
|
for (i = 0; i < chklen; i++) /* Copy the block check */
|
|
pbc[i] = p[datalen+i];
|
|
pbc[i] = '\0'; /* Null-terminate block check string */
|
|
#else
|
|
pbc[0] = p[datalen];
|
|
pbc[1] = '\0';
|
|
#endif /* F_CRC */
|
|
p[datalen] = '\0'; /* and the packet DATA field. */
|
|
#ifdef F_CRC
|
|
switch (chklen) { /* Check the block check */
|
|
case 1: /* Type 1, 6-bit checksum */
|
|
#endif /* F_CRC */
|
|
ok = (xunchar(*pbc) == chk1(q,k));
|
|
#ifdef DEBUG
|
|
if (ok && xerror()) ok = 0;
|
|
#endif /* DEBUG */
|
|
if (!ok) {
|
|
freerslot(k,r_slot);
|
|
#ifdef RECVONLY
|
|
nak(k,k->r_seq,r_slot);
|
|
#else
|
|
if (k->what == W_RECV)
|
|
nak(k,k->r_seq,r_slot);
|
|
else
|
|
resend(k);
|
|
#endif /* RECVONLY */
|
|
return(X_OK);
|
|
}
|
|
#ifdef F_CRC
|
|
break;
|
|
|
|
case 2: /* Type 2, 12-bit checksum */
|
|
i = xunchar(*pbc) << 6 | xunchar(pbc[1]);
|
|
ok = (i == chk2(q,k));
|
|
#ifdef DEBUG
|
|
if (ok && xerror()) ok = 0;
|
|
#endif /* DEBUG */
|
|
if (!ok) { /* No match */
|
|
if (t == 'E') { /* Allow E packets to have type 1 */
|
|
int j;
|
|
j = datalen;
|
|
p[j++] = pbc[0];
|
|
p[j] = '\0';
|
|
if (xunchar(pbc[1]) == chk1(q,k))
|
|
break;
|
|
else
|
|
p[--j] = '\0';
|
|
}
|
|
freerslot(k,r_slot);
|
|
#ifdef RECVONLY
|
|
nak(k,k->r_seq,r_slot);
|
|
#else
|
|
if (k->what == W_RECV)
|
|
nak(k,k->r_seq,r_slot);
|
|
else
|
|
resend(k);
|
|
#endif /* RECVONLY */
|
|
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(q,k));
|
|
#ifdef DEBUG
|
|
if (ok && xerror()) {
|
|
ok = 0;
|
|
debug(DB_MSG,"CRC ERROR INJECTED",0,0);
|
|
}
|
|
#endif /* DEBUG */
|
|
if (!ok) {
|
|
debug(DB_LOG,"CRC ERROR t",0,t);
|
|
if (t == 'E') { /* Allow E packets to have type 1 */
|
|
int j;
|
|
j = datalen;
|
|
p[j++] = pbc[0];
|
|
p[j++] = pbc[1];
|
|
p[j] = '\0';
|
|
if (xunchar(pbc[2]) == chk1(q,k))
|
|
break;
|
|
else { j -=2; p[j] = '\0'; }
|
|
}
|
|
freerslot(k,r_slot);
|
|
#ifdef RECVONLY
|
|
nak(k,k->r_seq,r_slot);
|
|
#else
|
|
if (k->what == W_RECV)
|
|
nak(k,k->r_seq,r_slot);
|
|
else
|
|
resend(k);
|
|
#endif /* RECVONLY */
|
|
return(X_OK);
|
|
}
|
|
}
|
|
#endif /* F_CRC */
|
|
if (t == 'E') /* (AND CLOSE FILES?) */
|
|
return(X_ERROR);
|
|
|
|
prev = k->r_seq - 1; /* Get sequence of previous packet */
|
|
if (prev < 0)
|
|
prev = 63;
|
|
|
|
debug(DB_LOG,"Seq",0,seq);
|
|
debug(DB_LOG,"Prev",0,prev);
|
|
|
|
if (seq == k->r_seq) { /* Is this the packet we want? */
|
|
k->ipktinfo[r_slot].rtr = 0; /* Yes */
|
|
} else {
|
|
freerslot(k,r_slot); /* No, discard it. */
|
|
|
|
if (seq == prev) { /* If it's the previous packet again */
|
|
debug(DB_LOG,"PREVIOUS PKT RETRIES",0,
|
|
(long)(k->ipktinfo[r_slot].rtr));
|
|
if (k->ipktinfo[r_slot].rtr++ > k->retry) { /* Count retries */
|
|
epkt("Too many retries", k); /* Too may */
|
|
return(X_ERROR); /* Give up */
|
|
} else { /* Otherwise */
|
|
return(resend(k)); /* Send old outbound packet buffer */
|
|
}
|
|
#ifdef RECVONLY
|
|
} else {
|
|
return(nak(k,k->r_seq,r_slot));
|
|
#else
|
|
} else if (k->what == W_RECV) { /* Otherwise NAK the one we want */
|
|
return(nak(k,k->r_seq,r_slot));
|
|
} else { /* or whatever... */
|
|
return(resend(k));
|
|
#endif /* RECVONLY */
|
|
}
|
|
}
|
|
#ifndef RECVONLY
|
|
if (k->what == W_SEND) { /* Sending, check for ACK */
|
|
if (t != 'Y') { /* Not an ACK */
|
|
debug(DB_LOG,"t!=Y t",0,t);
|
|
freerslot(k,r_slot); /* added 2004-06-30 -- JHD */
|
|
return(resend(k));
|
|
}
|
|
if (k->state == S_DATA) { /* ACK to Data packet?*/
|
|
if (k->cancel || /* Cancellation requested by caller? */
|
|
*p == 'X' || *p == 'Z') { /* Or by receiver? */
|
|
k->closef(k,*p,1); /* Close input file*/
|
|
nxtpkt(k); /* Next packet sequence number */
|
|
if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
|
|
return(rc);
|
|
if (*p == '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->status = S_EOF;
|
|
k->r_seq = k->s_seq; /* Sequence number of packet we want */
|
|
return(X_OK);
|
|
}
|
|
}
|
|
freerslot(k,r_slot); /* It is, free the ACK. */
|
|
}
|
|
#endif /* RECVONLY */
|
|
|
|
/* Now we have an incoming packet with the expected sequence number. */
|
|
|
|
debug(DB_CHR,"Packet OK",0,t);
|
|
debug(DB_LOG,"State",0,k->state);
|
|
|
|
switch (k->state) { /* Kermit protocol state switcher */
|
|
|
|
#ifndef RECVONLY
|
|
case S_INIT: /* Got other Kermit's parameters */
|
|
case S_EOF: /* Got ACK to EOF packet */
|
|
nxtpkt(k); /* Get next packet number etc */
|
|
if (k->state == S_INIT) { /* Got ACK to S packet? */
|
|
spar(k,p,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));
|
|
}
|
|
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 */
|
|
return(rc);
|
|
encstr(k->filename,k,r); /* Encode the name for transmission */
|
|
if ((rc = spkt('F',k->s_seq,-1,k->xdata,k)) != X_OK)
|
|
return(rc); /* Send F packet */
|
|
r->sofar = 0L;
|
|
k->state = S_FILE; /* Wait for ACK */
|
|
r->status = S_FILE;
|
|
} else { /* No more files - we're done */
|
|
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 */
|
|
r->status = 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 */
|
|
#ifdef F_AT
|
|
if (k->capas & CAP_AT) { /* A-packets negotiated? */
|
|
if ((rc = sattr(k,r)) != X_OK) /* Yes, send Attribute packet */
|
|
return(rc);
|
|
k->state = S_ATTR; /* And wait for its ACK */
|
|
r->status = S_ATTR;
|
|
} else
|
|
#endif /* F_AT */
|
|
if (sdata(k,r) == 0) { /* No A packets - send first data */
|
|
/* File is empty so send EOF packet */
|
|
if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
|
|
return(rc);
|
|
k->closef(k,*p,1); /* Close input file*/
|
|
k->state = S_EOF; /* Wait for ACK to EOF */
|
|
r->status = S_EOF;
|
|
} else { /* Sent some data */
|
|
k->state = S_DATA; /* Wait for ACK to first data */
|
|
r->status = S_DATA;
|
|
}
|
|
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 */
|
|
nxtpkt(k); /* Get next packet number */
|
|
if (k->state == S_ATTR) {
|
|
/* CHECK ATTRIBUTE RESPONSE */
|
|
/* IF REJECTED do the right thing... */
|
|
k->state = S_DATA;
|
|
r->status = S_DATA;
|
|
}
|
|
rc = sdata(k,r); /* Send first or next data packet */
|
|
|
|
debug(DB_LOG,"Seq",0,(k->s_seq));
|
|
debug(DB_LOG,"sdata()",0,rc);
|
|
|
|
if (rc == 0) { /* If there was no data to send */
|
|
if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
|
|
return(rc); /* Send EOF */
|
|
k->closef(k,*p,1); /* Close input file*/
|
|
k->state = S_EOF; /* And wait for ACK */
|
|
r->status = S_EOF;
|
|
} /* 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 */
|
|
return(X_DONE); /* (or X_ERROR) */
|
|
#endif /* RECVONLY */
|
|
|
|
case R_WAIT: /* Waiting for the S packet */
|
|
if (t == 'S') { /* Got it */
|
|
spar(k,p,datalen); /* Set parameters from it */
|
|
rc = rpar(k,'Y'); /* ACK with my parameters */
|
|
debug(DB_LOG,"rpar rc",0,rc);
|
|
if (rc != X_OK)
|
|
return(X_ERROR); /* I/O error, quit. */
|
|
k->state = R_FILE; /* All OK, switch states */
|
|
r->status = R_FILE;
|
|
} else { /* Wrong kind of packet, send NAK */
|
|
epkt("Unexpected packet type",k);
|
|
rc = X_ERROR;
|
|
}
|
|
freerslot(k,r_slot); /* Free packet slot */
|
|
return(rc);
|
|
|
|
case R_FILE: /* Want an F or B packet */
|
|
if (t == 'F') { /* File name */
|
|
if ((rc = decode(k, r, 0, p)) == X_OK) /* Decode and save */
|
|
k->state = R_ATTR; /* Switch to next state */
|
|
r->status = 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 */
|
|
rc = ack(k, k->r_seq, r->filename); /* so ACK the F packet */
|
|
} else {
|
|
epkt("Filename error",k); /* Error decoding filename */
|
|
}
|
|
} else if (t == 'B') { /* Break, end of transaction */
|
|
freerslot(k,r_slot);
|
|
rc = (ack(k, k->r_seq, (UCHAR *)0) == X_OK) ? X_DONE : X_ERROR;
|
|
|
|
} else {
|
|
epkt("Unexpected packet type",k);
|
|
rc = X_ERROR;
|
|
}
|
|
freerslot(k,r_slot);
|
|
return(rc);
|
|
|
|
case R_ATTR: /* Want A, D, or Z packet */
|
|
#ifdef F_AT
|
|
if (t == 'A') { /* Attribute packet */
|
|
int x;
|
|
x = gattr(k, p, r); /* Read the attributes */
|
|
if (x > -1)
|
|
k->binary = x;
|
|
freerslot(k,r_slot);
|
|
ack(k, k->r_seq, (UCHAR *) "Y"); /* Always accept the file */
|
|
return(X_OK);
|
|
} else
|
|
#endif /* F_AT */
|
|
if (t == 'D') { /* First data packet */
|
|
k->obufpos = 0; /* Initialize output buffer */
|
|
k->filename = r->filename;
|
|
r->sofar = 0L;
|
|
if ((rc = (*(k->openf))(k,r->filename, 2)) == X_OK) {
|
|
k->state = R_DATA; /* Switch to Data state */
|
|
r->status = k->state;
|
|
rc = decode(k, r, 1, p); /* Write out first data packet */
|
|
freerslot(k,r_slot);
|
|
} else {
|
|
epkt("File refused or can't be opened", k);
|
|
freerslot(k,r_slot);
|
|
return(rc);
|
|
}
|
|
if (rc == X_OK)
|
|
rc = ack(k, k->r_seq, s);
|
|
else
|
|
epkt("Error writing data", k);
|
|
return(rc);
|
|
} else if (t == '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 */
|
|
if ((rc = (*(k->openf))(k,r->filename, 2)) == X_OK) {
|
|
if (((rc = (*(k->closef))(k,*p,2)) == X_OK)) {
|
|
k->state = R_FILE;
|
|
rc = ack(k, k->r_seq, s);
|
|
} else {
|
|
epkt("Error closing empty file", k);
|
|
freerslot(k,r_slot);
|
|
return(rc);
|
|
}
|
|
} else {
|
|
epkt("File refused or can't be opened", k);
|
|
freerslot(k,r_slot);
|
|
return(rc);
|
|
}
|
|
r->status = k->state;
|
|
freerslot(k,r_slot);
|
|
|
|
} else {
|
|
epkt("Unexpected packet type",k);
|
|
return(X_ERROR);
|
|
}
|
|
break;
|
|
|
|
case R_DATA: /* Want a D or Z packet */
|
|
debug(DB_CHR,"R_DATA t",0,t);
|
|
if (t == 'D') { /* Data */
|
|
rc = decode(k, r, 1, p); /* Decode it */
|
|
freerslot(k,r_slot);
|
|
} else if (t == 'Z') { /* End of file */
|
|
debug(DB_CHR,"R_DATA",0,t);
|
|
if (k->obufpos > 0) { /* Flush output buffer */
|
|
rc = (*(k->writef))(k,k->obuf,k->obufpos);
|
|
debug(DB_LOG,"R_DATA writef rc",0,rc);
|
|
r->sofar += k->obufpos;
|
|
k->obufpos = 0;
|
|
}
|
|
if (((rc = (*(k->closef))(k,*p,2)) == X_OK) && (rc == X_OK))
|
|
k->state = R_FILE;
|
|
debug(DB_LOG,"R_DATA closef rc",0,rc);
|
|
r->status = k->state;
|
|
freerslot(k,r_slot);
|
|
} else {
|
|
epkt("Unexpected packet type",k);
|
|
return(X_ERROR);
|
|
}
|
|
if (rc == X_OK)
|
|
rc = ack(k, k->r_seq, s);
|
|
else
|
|
epkt(t == 'Z' ? "Can't close file" : "Error writing data",k);
|
|
return(rc);
|
|
|
|
case R_ERROR: /* Canceled from above */
|
|
default:
|
|
epkt(msg,k);
|
|
return(X_ERROR);
|
|
}
|
|
return(X_ERROR);
|
|
}
|
|
|
|
/* Utility routines */
|
|
|
|
UCHAR *
|
|
getrslot(struct k_data *k, short *n) { /* Find a free packet buffer */
|
|
register int i;
|
|
/*
|
|
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 (i = 0; i < P_WSLOTS; i++) { /* Search */
|
|
if (k->ipktinfo[i].len < 1) {
|
|
*n = i; /* Slot number */
|
|
k->ipktinfo[i].len = -1; /* Mark it as allocated but not used */
|
|
k->ipktinfo[i].seq = -1;
|
|
k->ipktinfo[i].typ = SP;
|
|
/* k->ipktinfo[i].rtr = 0; */ /* (see comment above) */
|
|
k->ipktinfo[i].dat = (UCHAR *)0;
|
|
return(&(k->ipktbuf[0][i]));
|
|
}
|
|
}
|
|
*n = -1;
|
|
return((UCHAR *)0);
|
|
}
|
|
|
|
void /* Initialize a window slot */
|
|
freerslot(struct k_data *k, short n) {
|
|
k->ipktinfo[n].len = 0; /* Packet length */
|
|
#ifdef COMMENT
|
|
k->ipktinfo[n].seq = 0; /* Sequence number */
|
|
k->ipktinfo[n].typ = (char)0; /* Type */
|
|
k->ipktinfo[n].rtr = 0; /* Retry count */
|
|
k->ipktinfo[n].flg = 0; /* Flags */
|
|
#endif /* COMMENT */
|
|
}
|
|
|
|
UCHAR *
|
|
getsslot(struct k_data *k, short *n) { /* Find a free packet buffer */
|
|
#ifdef COMMENT
|
|
register int i;
|
|
for (i = 0; i < P_WSLOTS; i++) { /* Search */
|
|
if (k->opktinfo[i].len < 1) {
|
|
*n = i; /* Slot number */
|
|
k->opktinfo[i].len = -1; /* Mark it as allocated but not used */
|
|
k->opktinfo[i].seq = -1;
|
|
k->opktinfo[i].typ = SP;
|
|
k->opktinfo[i].rtr = 0;
|
|
k->opktinfo[i].dat = (UCHAR *)0;
|
|
return(&(k->opktbuf[0][i]));
|
|
}
|
|
}
|
|
*n = -1;
|
|
return((UCHAR *)0);
|
|
#else
|
|
*n = 0;
|
|
return(k->opktbuf);
|
|
#endif /* COMMENT */
|
|
}
|
|
|
|
void /* Initialize a window slot */
|
|
freesslot(struct k_data * k, short n) {
|
|
k->opktinfo[n].len = 0; /* Packet length */
|
|
k->opktinfo[n].seq = 0; /* Sequence number */
|
|
k->opktinfo[n].typ = (char)0; /* Type */
|
|
k->opktinfo[n].rtr = 0; /* Retry count */
|
|
k->opktinfo[n].flg = 0; /* Flags */
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
#ifdef F_CRC
|
|
|
|
/* 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++) {
|
|
#ifdef COMMENT
|
|
c = crc ^ (long)(*pkt);
|
|
crc = (crc >> 8) ^ (k->crcta[(c & 0xF0) >> 4] ^ k->crctb[c & 0x0F]);
|
|
#else
|
|
c = crc ^ (*pkt);
|
|
crc = (crc >> 8) ^ ((k->crcta[(c & 0xF0) >> 4]) ^ (k->crctb[c & 0x0F]));
|
|
#endif /* COMMENT */
|
|
}
|
|
return(crc);
|
|
}
|
|
#endif /* F_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) {
|
|
|
|
unsigned int crc; /* For building CRC */
|
|
int i, j, lenpos, m, n, x; /* Workers */
|
|
UCHAR * s, * buf;
|
|
|
|
debug(DB_LOG,"spkt len 1",0,len);
|
|
if (len < 0) { /* Calculate data length ourselves? */
|
|
len = 0;
|
|
s = data;
|
|
while (*s++) len++;
|
|
}
|
|
debug(DB_LOG,"spkt len 2",0,len);
|
|
buf = k->opktbuf; /* Where to put packet (FOR NOW) */
|
|
|
|
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 */
|
|
j = len + k->bct;
|
|
#ifdef F_LP
|
|
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 */
|
|
#endif /* F_LP */
|
|
buf[lenpos] = tochar(j+2); /* Single-byte length in LEN field */
|
|
#ifdef F_LP
|
|
}
|
|
#endif /* F_LP */
|
|
if (data) /* Copy data, if any */
|
|
for ( ; len--; i++)
|
|
buf[i] = *data++;
|
|
buf[i] = '\0';
|
|
|
|
#ifdef F_CRC
|
|
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);
|
|
#ifdef XAC
|
|
/* HiTech's XAC compiler silently ruins the regular 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);
|
|
}
|
|
#else
|
|
buf[i++] = (unsigned)tochar((j >> 6) & 077);
|
|
buf[i++] = (unsigned)tochar(j & 077);
|
|
#endif /* XAC */
|
|
break;
|
|
case 3: /* 3 = 16-bit CRC */
|
|
crc = chk3(&buf[lenpos],k);
|
|
#ifdef XAC
|
|
/* 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);
|
|
}
|
|
#else
|
|
buf[i++] = (unsigned)tochar(((crc & 0170000)) >> 12);
|
|
buf[i++] = (unsigned)tochar((crc >> 6) & 077);
|
|
buf[i++] = (unsigned)tochar(crc & 077);
|
|
#endif /* XAC */
|
|
break;
|
|
}
|
|
#else
|
|
buf[i++] = tochar(chk1(&buf[lenpos],k));
|
|
#endif /* F_CRC */
|
|
|
|
buf[i++] = k->s_eom; /* Packet terminator */
|
|
buf[i] = '\0'; /* String terminator */
|
|
k->s_seq = seq; /* Remember sequence number */
|
|
|
|
k->opktlen = i; /* Remember length for retransmit */
|
|
|
|
#ifdef DEBUG
|
|
/* CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE */
|
|
if (xerror()) {
|
|
UCHAR p[P_PKTLEN+8];
|
|
int i;
|
|
for (i = 0; i < P_PKTLEN; i++)
|
|
if (!(p[i] = buf[i]))
|
|
break;
|
|
p[i-2] = 'X';
|
|
debug(DB_PKT,"XPKT",(char *)&p[1],0);
|
|
return((*(k->txd))(k,p,k->opktlen)); /* Send it. */
|
|
}
|
|
debug(DB_PKT,"SPKT",(char *)&buf[1],0);
|
|
#endif /* DEBUG */
|
|
|
|
return((*(k->txd))(k,buf,k->opktlen)); /* Send it. */
|
|
}
|
|
|
|
/* N A K -- Send a NAK (negative acknowledgement) */
|
|
|
|
STATIC int
|
|
nak(struct k_data * k, short seq, short slot) {
|
|
int rc;
|
|
rc = spkt('N', seq, 0, (UCHAR *)0, k);
|
|
if (k->ipktinfo[slot].rtr++ > k->retry)
|
|
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;
|
|
len = 0;
|
|
if (text) { /* Get length of data */
|
|
UCHAR *p;
|
|
p = text;
|
|
for ( ; *p++; len++) ;
|
|
}
|
|
rc = spkt('Y', seq, len, text, k); /* Send the packet */
|
|
debug(DB_LOG,"ack spkt rc",0,rc);
|
|
if (rc == X_OK) /* If OK */
|
|
k->r_seq = (k->r_seq + 1) % 64; /* bump the packet number */
|
|
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, y;
|
|
UCHAR c;
|
|
|
|
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) {
|
|
k->ebqflg = 1;
|
|
k->ebq = '&';
|
|
} else if (s[7] == 'N') {
|
|
/* WHAT? */
|
|
}
|
|
}
|
|
if (datalen >= 8) { /* Block check */
|
|
k->bct = s[8] - '0';
|
|
#ifdef F_CRC
|
|
if ((k->bct < 1) || (k->bct > 3))
|
|
#endif /* F_CRC */
|
|
k->bct = 1;
|
|
if (k->bctf) 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]);
|
|
|
|
#ifdef F_LP /* Long packets */
|
|
if (!(x & CAP_LP))
|
|
#endif /* F_LP */
|
|
k->capas &= ~CAP_LP;
|
|
|
|
#ifdef F_SW /* Sliding Windows */
|
|
if (!(x & CAP_SW))
|
|
#endif /* F_SW */
|
|
k->capas &= ~CAP_SW;
|
|
|
|
#ifdef F_AT /* Attributes */
|
|
if (!(x & CAP_AT))
|
|
#endif /* F_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++) ;
|
|
}
|
|
|
|
#ifdef F_LP /* Long Packets */
|
|
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;
|
|
}
|
|
}
|
|
#endif /* F_LP */
|
|
|
|
debug(DB_LOG,"S_MAXLEN",0,k->s_maxlen);
|
|
|
|
#ifdef F_SW
|
|
if (k->capas & CAP_SW) {
|
|
if (datalen > y) {
|
|
x = xunchar(s[y+1]);
|
|
k->window = (x > P_WSLOTS) ? P_WSLOTS : x;
|
|
if (k->window < 1) /* Watch out for bad negotiation */
|
|
k->window = 1;
|
|
if (k->window > 1)
|
|
if (k->window > k->retry) /* Retry limit must be greater */
|
|
k->retry = k->window + 1; /* than window size. */
|
|
}
|
|
}
|
|
#endif /* F_SW */
|
|
}
|
|
|
|
/* R P A R -- Send my parameters to other Kermit */
|
|
|
|
STATIC int
|
|
rpar(struct k_data * k, char type) {
|
|
UCHAR *d;
|
|
int rc, len;
|
|
#ifdef F_CRC
|
|
short b;
|
|
#endif /* F_CRC */
|
|
|
|
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->bctf) /* Block check type */
|
|
d[7] = '5'; /* FORCE 3 */
|
|
else
|
|
d[7] = k->bct + '0'; /* Normal */
|
|
d[ 8] = k->rptq; /* Repeat prefix */
|
|
d[ 9] = tochar(k->capas); /* Capability bits */
|
|
d[10] = tochar(k->window); /* Window size */
|
|
|
|
#ifdef F_LP
|
|
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;
|
|
#else
|
|
d[11] = '\0';
|
|
len = 11;
|
|
#endif /* F_LP */
|
|
|
|
#ifdef F_CRC
|
|
if (!(k->bctf)) { /* Unless FORCE 3 */
|
|
b = k->bct;
|
|
k->bct = 1; /* Always use block check type 1 */
|
|
}
|
|
#endif /* F_CRC */
|
|
switch (type) {
|
|
case 'Y': /* This is an ACK for packet 0 */
|
|
rc = ack(k,0,d);
|
|
break;
|
|
case 'S': /* It's an S packet */
|
|
rc = spkt('S', 0, len, d, k);
|
|
break;
|
|
default:
|
|
rc = -1;
|
|
}
|
|
#ifdef F_CRC
|
|
if (!(k->bctf)) { /* Unless FORCE 3 */
|
|
k->bct = b;
|
|
}
|
|
#endif /* F_CRC */
|
|
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) {
|
|
|
|
register unsigned int a, a7; /* Current character */
|
|
unsigned int b8; /* 8th bit */
|
|
int rpt; /* Repeat count */
|
|
int rc; /* Return code */
|
|
UCHAR *p;
|
|
|
|
rc = X_OK;
|
|
rpt = 0; /* Initialize repeat count. */
|
|
if (f == 0) /* Output function... */
|
|
p = r->filename;
|
|
|
|
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 */
|
|
|
|
if (rpt == 0) rpt = 1; /* If no repeats, then one */
|
|
|
|
for (; rpt > 0; rpt--) { /* Output the char 'rpt' times */
|
|
if (f == 0) {
|
|
*p++ = (UCHAR) a; /* to memory */
|
|
} else { /* or to file */
|
|
k->obuf[k->obufpos++] = (UCHAR) a; /* Deposit the byte */
|
|
if (k->obufpos == k->obuflen) { /* Buffer full? */
|
|
rc = (*(k->writef))(k,k->obuf,k->obuflen); /* Dump it. */
|
|
r->sofar += k->obuflen;
|
|
if (rc != X_OK) break;
|
|
k->obufpos = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (f == 0) /* If writing to memory */
|
|
*p = '\0'; /* terminate the string */
|
|
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);
|
|
}
|
|
|
|
#ifdef F_AT
|
|
|
|
/*
|
|
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, fsizek; /* 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 size",sizebuf,0);
|
|
}
|
|
break;
|
|
|
|
case '#': /* File creation date */
|
|
for (i = 0; (i < aln) && (i < DATE_MAX); i++)
|
|
r->filedate[i] = *s++; /* Copy it into a static 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;
|
|
short tmp;
|
|
long filelength;
|
|
UCHAR datebuf[DATE_MAX], * p;
|
|
|
|
debug(DB_LOG,"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,"sattr filename: ",k->filename,0);
|
|
debug(DB_LOG,"sattr filedate: ",datebuf,0);
|
|
debug(DB_LOG,"sattr filelength",0,filelength);
|
|
debug(DB_LOG,"sattr 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_LOG,"sattr k->xdata: ",k->xdata,0);
|
|
return(spkt('A',k->s_seq,-1,k->xdata,k));
|
|
}
|
|
#endif /* F_AT */
|
|
|
|
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 */
|
|
|
|
debug(DB_LOG,"getpkt k->s_first",0,(k->s_first));
|
|
debug(DB_LOG,"getpkt k->s_remain=",k->s_remain,0);
|
|
|
|
maxlen = k->s_maxlen - k->bct - 3; /* 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 DEBUG
|
|
k->zincnt = -1234;
|
|
k->dummy = 0;
|
|
#endif /* DEBUG */
|
|
c = zgetc();
|
|
|
|
#ifdef DEBUG
|
|
if (k->dummy) debug(DB_LOG,"DUMMY CLOBBERED (A)",0,0);
|
|
#endif /* DEBUG */
|
|
}
|
|
if (c < 0) { /* Watch out for empty file. */
|
|
debug(DB_CHR,"getpkt first c",0,c);
|
|
k->s_first = -1;
|
|
return(k->size = 0);
|
|
}
|
|
r->sofar++;
|
|
debug(DB_LOG,"getpkt first c",0,c);
|
|
} else if (k->s_first == -1 && !k->s_remain[0]) { /* EOF from last time? */
|
|
return(k->size = 0);
|
|
}
|
|
for (k->size = 0;
|
|
(k->xdata[k->size] = k->s_remain[k->size]) != '\0';
|
|
(k->size)++)
|
|
;
|
|
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 DEBUG
|
|
k->dummy = 0;
|
|
#endif /* DEBUG */
|
|
next = zgetc();
|
|
#ifdef DEBUG
|
|
if (k->dummy) debug(DB_LOG,"DUMMY CLOBBERED B",0,k->dummy);
|
|
#endif /* DEBUG */
|
|
}
|
|
if (next < 0) { /* If none, we're at EOF. */
|
|
k->s_first = -1;
|
|
} else { /* Otherwise */
|
|
r->sofar++; /* 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. */
|
|
|
|
if (k->size == maxlen) /* Just at end, done. */
|
|
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++)
|
|
;
|
|
k->size = k->osize;
|
|
k->xdata[k->size] = '\0';
|
|
return(k->size); /* Return size. */
|
|
}
|
|
}
|
|
return(k->size); /* EOF, return size. */
|
|
}
|
|
|
|
#ifndef RECVONLY
|
|
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",0,len);
|
|
if (len < 1)
|
|
return(0);
|
|
rc = spkt('D',k->s_seq,len,k->xdata,k); /* Send the packet */
|
|
debug(DB_LOG,"sdata spkt",0,rc);
|
|
return((rc == X_ERROR) ? rc : len);
|
|
}
|
|
#endif /* RECVONLY */
|
|
|
|
/* E P K T -- Send a (fatal) Error packet with the given message */
|
|
|
|
STATIC void
|
|
epkt(char * msg, struct k_data * k) {
|
|
if (!(k->bctf)) { /* Unless FORCE 3 */
|
|
k->bct = 1;
|
|
}
|
|
(void) spkt('E', 0, -1, (UCHAR *) msg, k);
|
|
}
|
|
|
|
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 */
|
|
|
|
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);
|
|
*(k->ostring) = '\0'; /* Terminate with null */
|
|
k->ostring = (UCHAR *)0; /* Reset output string pointer */
|
|
}
|
|
|
|
STATIC void
|
|
encode(int a, int next, struct k_data * k) { /* Encode character into packet */
|
|
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 */
|
|
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. */
|
|
}
|
|
|
|
STATIC int
|
|
nxtpkt(struct k_data * k) { /* Get next packet to send */
|
|
k->s_seq = (k->s_seq + 1) & 63; /* Next sequence number */
|
|
k->xdata = k->xdatabuf;
|
|
return(0);
|
|
}
|
|
|
|
STATIC int
|
|
resend(struct k_data * k) {
|
|
UCHAR * buf;
|
|
if (!k->opktlen) /* Nothing to resend */
|
|
return(X_OK);
|
|
buf = k->opktbuf;
|
|
debug(DB_PKT,">PKT",&buf[1],k->opktlen);
|
|
return((*(k->txd))(k,buf,k->opktlen));
|
|
}
|