#include "uucp.h"
#if USE_RCS_ID
const char protg_rcsid[] = "$Id: protg.c,v 1.71 2002/03/05 19:10:41 ian Rel $";
#endif
#include <ctype.h>
#include <errno.h>
#include "uudefs.h"
#include "uuconf.h"
#include "conn.h"
#include "trans.h"
#include "system.h"
#include "prot.h"
#define IFRAME_DLE (0)
#define IFRAME_K (1)
#define IFRAME_CHECKLOW (2)
#define IFRAME_CHECKHIGH (3)
#define IFRAME_CONTROL (4)
#define IFRAME_XOR (5)
#define CFRAMELEN (6)
#define CONTROL_TT(b) ((int)(((b) >> 6) & 03))
#define CONTROL_XXX(b) ((int)(((b) >> 3) & 07))
#define CONTROL_YYY(b) ((int)((b) & 07))
#define DLE ('\020')
#define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4)))
#define KCONTROL (9)
#define INEXTSEQ(i) ((i + 1) & 07)
#define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07)
#define CONTROL (0)
#define ALTCHAN (1)
#define DATA (2)
#define SHORTDATA (3)
#define CLOSE (1)
#define RJ (2)
#define SRJ (3)
#define RR (4)
#define INITC (5)
#define INITB (6)
#define INITA (7)
#define CMAXDATAINDEX (8)
#define CMAXDATA (1 << (CMAXDATAINDEX + 4))
#define CMAXWINDOW (7)
#define IWINDOW (7)
#define IPACKSIZE (64)
#define CSTARTUP_RETRIES (8)
#define CEXCHANGE_INIT_TIMEOUT (10)
#define CEXCHANGE_INIT_RETRIES (4)
#define CTIMEOUT (10)
#define CRETRIES (6)
#define CGARBAGE (10000)
#define CERRORS (100)
#define CERROR_DECAY (10)
#define IREMOTE_WINDOW (0)
#define IREMOTE_PACKSIZE (0)
static int iGsendseq;
static int iGremote_ack;
static int iGretransmit_seq;
static int iGrecseq;
static int iGlocal_ack;
static int iGrequest_winsize = IWINDOW;
static int iGrequest_packsize = IPACKSIZE;
static int iGremote_winsize;
static int iGforced_remote_winsize = IREMOTE_WINDOW;
static int iGremote_segsize;
static size_t iGremote_packsize;
static int iGforced_remote_packsize = IREMOTE_PACKSIZE;
static int iGpacket_control;
static int cGstartup_retries = CSTARTUP_RETRIES;
static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
static int cGtimeout = CTIMEOUT;
static int cGretries = CRETRIES;
static int cGgarbage_data = CGARBAGE;
static int cGmax_errors = CERRORS;
static int cGerror_decay = CERROR_DECAY;
static boolean fGshort_packets = TRUE;
struct uuconf_cmdtab asGproto_params[] =
{
{ "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL },
{ "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize,
NULL },
{ "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries,
NULL },
{ "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout,
NULL },
{ "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries,
NULL },
{ "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL },
{ "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL },
{ "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL },
{ "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL },
{ "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL },
{ "remote-window", UUCONF_CMDTABTYPE_INT,
(pointer) &iGforced_remote_winsize, NULL },
{ "remote-packet-size", UUCONF_CMDTABTYPE_INT,
(pointer) &iGforced_remote_packsize, NULL },
{ "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets,
NULL },
{ NULL, 0, NULL, NULL }
};
static long cGsent_packets;
static long cGresent_packets;
static long cGdelayed_packets;
static long cGrec_packets;
static long cGbad_hdr;
static long cGbad_checksum;
static long cGbad_order;
static long cGremote_rejects;
static long cGremote_duprrs;
static long cGerror_level;
static int cGexpect_bad_order;
#if DEBUG > 1
static const char * const azGcontrol[] =
{"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
#endif
static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl,
int ival, int *piset));
static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl,
int ival));
static char *zgadjust_ack P((int iseq));
static boolean fgwait_for_packet P((struct sdaemon *qdaemon,
boolean freturncontrol, int ctimeout,
int cretries));
static boolean fgsend_acks P((struct sdaemon *qdaemon));
static boolean fggot_ack P((struct sdaemon *qdaemon, int iack));
static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks,
boolean freturncontrol,
boolean *pfexit, size_t *pcneed,
boolean *pffound));
static boolean fginit_sendbuffers P((boolean fallocate));
static boolean fgcheck_errors P((struct sdaemon *qdaemon));
static int igchecksum P((const char *zdata, size_t clen));
static int igchecksum2 P((const char *zfirst, size_t cfirst,
const char *zsecond, size_t csecond));
boolean
fgstart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
int iseg;
int i;
boolean fgota, fgotb;
*pzlog = NULL;
if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
return FALSE;
iGsendseq = 1;
iGremote_ack = 0;
iGretransmit_seq = -1;
iGrecseq = 0;
iGlocal_ack = 0;
cGsent_packets = 0;
cGresent_packets = 0;
cGdelayed_packets = 0;
cGrec_packets = 0;
cGbad_hdr = 0;
cGbad_checksum = 0;
cGbad_order = 0;
cGremote_rejects = 0;
cGremote_duprrs = 0;
cGerror_level = 0;
cGexpect_bad_order = 0;
i = iGrequest_packsize;
iseg = -1;
while (i > 0)
{
++iseg;
i >>= 1;
}
iseg -= 5;
if (iseg < 0 || iseg > 7)
{
ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol",
iGrequest_packsize, qdaemon->qproto->bname);
iseg = 1;
}
if (iGrequest_winsize <= 0 || iGrequest_winsize > 7)
{
ulog (LOG_ERROR, "Illegal window size %d for '%c' protocol",
iGrequest_winsize, qdaemon->qproto->bname);
iGrequest_winsize = IWINDOW;
}
fgota = FALSE;
fgotb = FALSE;
for (i = 0; i < cGstartup_retries; i++)
{
if (fgota)
{
if (! fgsend_control (qdaemon, INITA, iGrequest_winsize))
return FALSE;
}
else
{
if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize,
&iGremote_winsize))
continue;
}
fgota = TRUE;
if (fgotb)
{
if (! fgsend_control (qdaemon, INITB, iseg))
return FALSE;
}
else
{
if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize))
continue;
}
fgotb = TRUE;
if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize,
&iGremote_winsize))
continue;
iGremote_packsize = 1 << (iGremote_segsize + 5);
if (iGforced_remote_winsize > 0
&& iGforced_remote_winsize <= CMAXWINDOW)
iGremote_winsize = iGforced_remote_winsize;
if (iGforced_remote_packsize >= 32
&& iGforced_remote_packsize <= 4096)
{
i = iGforced_remote_packsize;
iseg = -1;
while (i > 0)
{
++iseg;
i >>= 1;
}
iGremote_packsize = 1 << iseg;
iGremote_segsize = iseg - 5;
}
if (! fginit_sendbuffers (TRUE))
return FALSE;
*pzlog =
zbufalc (sizeof "protocol '' sending packet/window / receiving /"
+ 64);
sprintf (*pzlog,
"protocol '%c' sending packet/window %d/%d receiving %d/%d",
qdaemon->qproto->bname, (int) iGremote_packsize,
(int) iGremote_winsize, (int) iGrequest_packsize,
(int) iGrequest_winsize);
return TRUE;
}
DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgstart: Protocol startup failed");
return FALSE;
}
boolean
fbiggstart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
fGshort_packets = FALSE;
return fgstart (qdaemon, pzlog);
}
boolean
fvstart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
if (iGrequest_packsize == IPACKSIZE)
iGrequest_packsize = 1024;
return fgstart (qdaemon, pzlog);
}
static boolean
fgexchange_init (qdaemon, ictl, ival, piset)
struct sdaemon *qdaemon;
int ictl;
int ival;
int *piset;
{
int i;
for (i = 0; i < cGexchange_init_retries; i++)
{
long itime;
int ctimeout;
if (qdaemon->fcaller || i > 0)
{
if (! fgsend_control (qdaemon, ictl, ival))
return FALSE;
}
itime = ixsysdep_time ((long *) NULL);
ctimeout = cGexchange_init_timeout;
do
{
long inewtime;
if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0))
break;
if (CONTROL_TT (iGpacket_control) == CONTROL)
{
if (CONTROL_XXX (iGpacket_control) == ictl)
{
*piset = CONTROL_YYY (iGpacket_control);
if (! qdaemon->fcaller && i == 0)
{
if (! fgsend_control (qdaemon, ictl, ival))
return FALSE;
}
return TRUE;
}
if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA)
return FALSE;
if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC)
return FALSE;
if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC)
iGremote_segsize = CONTROL_YYY (iGpacket_control);
}
inewtime = ixsysdep_time ((long *) NULL);
ctimeout -= inewtime - itime;
}
while (ctimeout > 0);
}
return FALSE;
}
boolean
fgshutdown (qdaemon)
struct sdaemon *qdaemon;
{
(void) fgsend_control (qdaemon, CLOSE, 0);
(void) fgsend_control (qdaemon, CLOSE, 0);
(void) fginit_sendbuffers (FALSE);
ulog (LOG_NORMAL,
"Protocol '%c' packets: sent %ld, resent %ld, received %ld",
qdaemon->qproto->bname, cGsent_packets,
cGresent_packets - cGdelayed_packets, cGrec_packets);
if (cGbad_hdr != 0
|| cGbad_checksum != 0
|| cGbad_order != 0
|| cGremote_rejects != 0
|| cGremote_duprrs != 0)
ulog (LOG_NORMAL,
"Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
cGbad_hdr, cGbad_checksum, cGbad_order,
cGremote_duprrs + cGremote_rejects);
iGrequest_winsize = IWINDOW;
iGrequest_packsize = IPACKSIZE;
cGstartup_retries = CSTARTUP_RETRIES;
cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
cGtimeout = CTIMEOUT;
cGretries = CRETRIES;
cGgarbage_data = CGARBAGE;
cGmax_errors = CERRORS;
cGerror_decay = CERROR_DECAY;
iGforced_remote_winsize = IREMOTE_WINDOW;
iGforced_remote_packsize = IREMOTE_PACKSIZE;
fGshort_packets = TRUE;
return TRUE;
}
boolean
fgsendcmd (qdaemon, z, ilocal, iremote)
struct sdaemon *qdaemon;
const char *z;
int ilocal ATTRIBUTE_UNUSED;
int iremote ATTRIBUTE_UNUSED;
{
size_t clen;
boolean fagain;
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z);
clen = strlen (z);
do
{
char *zpacket;
size_t cdummy;
zpacket = zggetspace (qdaemon, &cdummy);
if (clen < iGremote_packsize)
{
size_t csize;
if (iGremote_packsize <= 64 || ! fGshort_packets)
csize = iGremote_packsize;
else
{
csize = 32;
while (csize <= clen)
csize <<= 1;
}
memcpy (zpacket, z, clen);
if (csize > clen)
bzero (zpacket + clen, csize - clen);
fagain = FALSE;
if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0))
return FALSE;
}
else
{
memcpy (zpacket, z, iGremote_packsize);
z += iGremote_packsize;
clen -= iGremote_packsize;
fagain = TRUE;
if (! fgsenddata (qdaemon, zpacket, iGremote_packsize,
0, 0, (long) 0))
return FALSE;
}
}
while (fagain);
return TRUE;
}
#define CSENDBUFFERS (CMAXWINDOW + 1)
static char *azGsendbuffers[CSENDBUFFERS];
static boolean
fginit_sendbuffers (fallocate)
boolean fallocate;
{
int i;
for (i = 0; i < CSENDBUFFERS; i++)
{
xfree ((pointer) azGsendbuffers[i]);
if (fallocate)
{
azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2
+ iGremote_packsize);
if (azGsendbuffers[i] == NULL)
return FALSE;
bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize);
}
else
azGsendbuffers[i] = NULL;
}
return TRUE;
}
char *
zggetspace (qdaemon, pclen)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
size_t *pclen;
{
*pclen = iGremote_packsize;
return azGsendbuffers[iGsendseq] + CFRAMELEN + 2;
}
boolean
fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
struct sdaemon *qdaemon;
char *zdata;
size_t cdata;
int ilocal ATTRIBUTE_UNUSED;
int iremote ATTRIBUTE_UNUSED;
long ipos ATTRIBUTE_UNUSED;
{
char *z;
int itt, iseg;
size_t csize;
int iclr1, iclr2;
unsigned short icheck;
itt = DATA;
csize = iGremote_packsize;
iseg = iGremote_segsize + 1;
#if DEBUG > 0
if (cdata > csize)
ulog (LOG_FATAL, "fgsend_packet: Packet size too large");
#endif
iclr1 = -1;
iclr2 = -2;
if (cdata < csize)
{
if (iGremote_packsize > 64 && fGshort_packets)
{
iseg = 1;
csize = 32;
while (csize < cdata)
{
csize <<= 1;
++iseg;
}
}
if (csize != cdata)
{
size_t cshort;
iclr2 = 0;
itt = SHORTDATA;
cshort = csize - cdata;
if (cshort <= 127)
{
--zdata;
zdata[0] = (char) cshort;
zdata[-1] = '\0';
if (cshort > 1)
bzero (zdata + cdata + 1, cshort - 1);
}
else
{
zdata -= 2;
zdata[0] = (char) (0x80 | (cshort & 0x7f));
zdata[1] = (char) (cshort >> 7);
bzero (zdata + cdata + 2, cshort - 2);
iclr1 = 0;
}
}
}
z = zdata - CFRAMELEN;
z[iclr1] = '\0';
z[iclr2] = '\0';
z[IFRAME_DLE] = DLE;
z[IFRAME_K] = (char) iseg;
icheck = (unsigned short) igchecksum (zdata, csize);
while (iGsendseq == iGremote_ack
|| CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize)
{
if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries))
return FALSE;
}
while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1)
{
iGlocal_ack = INEXTSEQ (iGlocal_ack);
if (! fgsend_control (qdaemon, RR, iGlocal_ack))
return FALSE;
}
iGlocal_ack = iGrecseq;
z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq);
iGsendseq = INEXTSEQ (iGsendseq);
icheck = ((unsigned short)
((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
++cGsent_packets;
if (iGretransmit_seq != -1)
{
++cGdelayed_packets;
return TRUE;
}
DEBUG_MESSAGE2 (DEBUG_PROTO,
"fgsenddata: Sending packet %d (%d bytes)",
CONTROL_XXX (z[IFRAME_CONTROL]), cdata);
return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE);
}
static char *
zgadjust_ack (iseq)
int iseq;
{
register char *z;
unsigned short icheck;
z = azGsendbuffers[iseq];
if (*z == '\0')
++z;
if (*z == '\0')
++z;
if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq)
return z;
icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8)
| (z[IFRAME_CHECKLOW] & 0xff));
icheck = ((unsigned short)
(((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff));
z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq);
icheck = ((unsigned short)
((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
return z;
}
static boolean
fgsend_control (qdaemon, ixxx, iyyy)
struct sdaemon *qdaemon;
int ixxx;
int iyyy;
{
char ab[CFRAMELEN];
int ictl;
unsigned short icheck;
#if DEBUG > 1
if (FDEBUGGING (DEBUG_PROTO) ||
(FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR))
ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d",
azGcontrol[ixxx], iyyy);
#endif
ab[IFRAME_DLE] = DLE;
ab[IFRAME_K] = KCONTROL;
ictl = (CONTROL << 6) | (ixxx << 3) | iyyy;
icheck = (unsigned short) (0xaaaa - ictl);
ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
ab[IFRAME_CONTROL] = (char) ictl;
ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]);
return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE);
}
boolean
fgwait (qdaemon)
struct sdaemon *qdaemon;
{
return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries);
}
static boolean
fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries)
struct sdaemon *qdaemon;
boolean freturncontrol;
int ctimeout;
int cretries;
{
int ctimeouts;
int cgarbage;
int cshort;
ctimeouts = 0;
cgarbage = 0;
cshort = 0;
while (TRUE)
{
boolean fexit;
size_t cneed;
boolean ffound;
size_t crec;
if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit,
&cneed, &ffound))
return FALSE;
if (fexit)
return TRUE;
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fgwait_for_packet: Need %lu bytes",
(unsigned long) cneed);
if (ffound)
{
ctimeouts = 0;
cgarbage = 0;
}
else
{
if (cgarbage > cGgarbage_data)
{
ulog (LOG_ERROR, "Too much unrecognized data");
return FALSE;
}
}
if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
return FALSE;
cgarbage += crec;
if (crec != 0)
{
if (crec >= cneed)
cshort = 0;
else
{
++cshort;
if (cshort > 1)
{
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
cshort = 0;
}
}
}
else
{
++ctimeouts;
if (ctimeouts > cretries)
{
if (cretries > 0)
ulog (LOG_ERROR, "Timed out waiting for packet");
return FALSE;
}
if (INEXTSEQ (iGremote_ack) != iGsendseq)
{
int inext;
char *zsend;
inext = INEXTSEQ (iGremote_ack);
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgwait_for_packet: Resending packet %d",
inext);
++cGresent_packets;
zsend = zgadjust_ack (inext);
if (! fsend_data (qdaemon->qconn, zsend,
CFRAMELEN + CPACKLEN (zsend), TRUE))
return FALSE;
iGretransmit_seq = inext;
}
else
{
if (iGlocal_ack != iGrecseq)
{
if (! fgsend_acks (qdaemon))
return FALSE;
}
if (! fgsend_control (qdaemon, RJ, iGrecseq))
return FALSE;
}
}
}
}
static boolean
fgsend_acks (qdaemon)
struct sdaemon *qdaemon;
{
while (iGlocal_ack != iGrecseq)
{
iGlocal_ack = INEXTSEQ (iGlocal_ack);
if (! fgsend_control (qdaemon, RR, iGlocal_ack))
return FALSE;
}
return TRUE;
}
static boolean
fggot_ack (qdaemon, iack)
struct sdaemon *qdaemon;
int iack;
{
int inext;
char *zsend;
if (cGerror_level > 0
&& iGretransmit_seq == -1
&& cGsent_packets % cGerror_decay == 0)
--cGerror_level;
cGexpect_bad_order = 0;
if (iack < iGremote_ack)
uwindow_acked (qdaemon, FALSE);
iGremote_ack = iack;
if (iGretransmit_seq == -1)
return TRUE;
inext = INEXTSEQ (iGretransmit_seq);
if (inext == iGsendseq)
iGretransmit_seq = -1;
else
{
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fggot_ack: Sending packet %d", inext);
++cGresent_packets;
zsend = zgadjust_ack (inext);
if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend),
TRUE))
return FALSE;
inext = INEXTSEQ (inext);
if (inext == iGsendseq)
iGretransmit_seq = -1;
else
{
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fggot_ack: Sending packet %d", inext);
++cGresent_packets;
zsend = zgadjust_ack (inext);
if (! fsend_data (qdaemon->qconn, zsend,
CFRAMELEN + CPACKLEN (zsend), TRUE))
return FALSE;
iGretransmit_seq = inext;
}
}
return TRUE;
}
static boolean
fgcheck_errors (qdaemon)
struct sdaemon *qdaemon;
{
if (cGerror_level > cGmax_errors && cGmax_errors >= 0)
{
ulog (LOG_ERROR, "Too many '%c' protocol errors",
qdaemon->qproto->bname);
return FALSE;
}
return TRUE;
}
static boolean
fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound)
struct sdaemon *qdaemon;
boolean fdoacks;
boolean freturncontrol;
boolean *pfexit;
size_t *pcneed;
boolean *pffound;
{
*pfexit = FALSE;
if (pffound != NULL)
*pffound = FALSE;
while (iPrecstart != iPrecend)
{
char ab[CFRAMELEN];
int i, iget, cwant;
unsigned short ihdrcheck, idatcheck;
const char *zfirst, *zsecond;
int cfirst, csecond;
boolean fduprr;
if (abPrecbuf[iPrecstart] != DLE)
{
char *zdle;
cfirst = iPrecend - iPrecstart;
if (cfirst < 0)
cfirst = CRECBUFLEN - iPrecstart;
zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst);
if (zdle == NULL)
{
iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN;
continue;
}
iPrecstart += zdle - (abPrecbuf + iPrecstart);
}
for (i = 0, iget = iPrecstart;
i < CFRAMELEN && iget != iPrecend;
i++, iget = (iget + 1) % CRECBUFLEN)
ab[i] = abPrecbuf[iget];
if (i < CFRAMELEN)
{
if (pcneed != NULL)
*pcneed = CFRAMELEN - i;
return TRUE;
}
if (ab[IFRAME_DLE] != DLE
|| ab[IFRAME_K] < 1
|| ab[IFRAME_K] > 9
|| ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL])
|| CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN)
{
++cGbad_hdr;
++cGerror_level;
DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d",
ab[IFRAME_K] & 0xff,
CONTROL_TT (ab[IFRAME_CONTROL]),
ab[IFRAME_XOR] & 0xff,
(ab[IFRAME_K]
^ ab[IFRAME_CHECKLOW]
^ ab[IFRAME_CHECKHIGH]
^ ab[IFRAME_CONTROL]) & 0xff);
if (! fgcheck_errors (qdaemon))
return FALSE;
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
zfirst = abPrecbuf + iPrecstart + CFRAMELEN;
cfirst = 0;
zsecond = NULL;
csecond = 0;
if (ab[IFRAME_K] == KCONTROL)
{
if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
{
++cGbad_hdr;
++cGerror_level;
DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Bad header: control packet with data");
if (! fgcheck_errors (qdaemon))
return FALSE;
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]);
cwant = 0;
}
else
{
int cinbuf;
unsigned short icheck;
if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL)
{
++cGbad_hdr;
++cGerror_level;
DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Bad header: data packet is type CONTROL");
if (! fgcheck_errors (qdaemon))
return FALSE;
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
cinbuf = iPrecend - iPrecstart;
if (cinbuf < 0)
cinbuf += CRECBUFLEN;
cinbuf -= CFRAMELEN;
cwant = (int) CPACKLEN (ab);
if (cinbuf < cwant)
{
if (pcneed != NULL)
*pcneed = cwant - cinbuf;
return TRUE;
}
if (iPrecend >= iPrecstart)
cfirst = cwant;
else
{
cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN);
if (cfirst >= cwant)
cfirst = cwant;
else if (cfirst > 0)
{
zsecond = abPrecbuf;
csecond = cwant - cfirst;
}
else
{
zfirst = abPrecbuf - cfirst;
cfirst = cwant;
}
}
if (csecond == 0)
icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst);
else
icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst,
zsecond,
(size_t) csecond);
idatcheck = ((unsigned short)
(((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff)))
& 0xffff)));
}
ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8)
| (ab[IFRAME_CHECKLOW] & 0xff));
if (ihdrcheck != idatcheck)
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Bad checksum: header 0x%x, data 0x%x",
ihdrcheck, idatcheck);
++cGbad_checksum;
++cGerror_level;
if (! fgcheck_errors (qdaemon))
return FALSE;
if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
{
if (iGrecseq != iGlocal_ack)
{
if (! fgsend_acks (qdaemon))
return FALSE;
}
if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
{
if (! fgsend_control (qdaemon, RJ, iGrecseq))
return FALSE;
cGexpect_bad_order += iGrequest_winsize - 1;
}
}
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN;
iGpacket_control = ab[IFRAME_CONTROL] & 0xff;
fduprr = FALSE;
if (cGremote_rejects == 0
&& CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL
&& CONTROL_XXX (ab[IFRAME_CONTROL]) == RR
&& iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL])
&& INEXTSEQ (iGremote_ack) != iGsendseq
&& iGretransmit_seq == -1)
{
DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Treating duplicate RR as RJ");
fduprr = TRUE;
}
if ((CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL
&& CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
|| (CONTROL_XXX (ab[IFRAME_CONTROL]) == RR && ! fduprr))
{
if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL])))
return FALSE;
}
if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
{
if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq))
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Got packet %d; expected %d",
CONTROL_XXX (ab[IFRAME_CONTROL]),
INEXTSEQ (iGrecseq));
if (cGexpect_bad_order > 0)
--cGexpect_bad_order;
else
{
++cGbad_order;
++cGerror_level;
if (! fgcheck_errors (qdaemon))
return FALSE;
}
continue;
}
++cGrec_packets;
if (cGerror_level > 0
&& cGrec_packets % cGerror_decay == 0)
--cGerror_level;
cGexpect_bad_order = 0;
iGrecseq = INEXTSEQ (iGrecseq);
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fgprocess_data: Got packet %d", iGrecseq);
if (pffound != NULL)
*pffound = TRUE;
if (fdoacks)
{
if (! fgsend_acks (qdaemon))
return FALSE;
}
if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA)
{
int cshort, cmove;
if ((zfirst[0] & 0x80) == 0)
{
cshort = zfirst[0] & 0xff;
cmove = 1;
}
else
{
int cbyte2;
if (cfirst > 1)
cbyte2 = zfirst[1] & 0xff;
else
cbyte2 = zsecond[0] & 0xff;
cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7);
cmove = 2;
}
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fgprocess_data: Packet short by %d",
cshort);
if (cfirst > cmove)
{
zfirst += cmove;
cfirst -= cmove;
}
else
{
zfirst = zsecond + (cmove - cfirst);
cfirst = csecond - (cmove - cfirst);
csecond = 0;
}
cshort -= cmove;
if (csecond >= cshort)
csecond -= cshort;
else
{
cfirst -= cshort - csecond;
csecond = 0;
}
#if DEBUG > 0
if (cfirst < 0)
cfirst = 0;
#endif
}
if (! fgot_data (qdaemon, zfirst, (size_t) cfirst,
zsecond, (size_t) csecond,
-1, -1, (long) -1,
INEXTSEQ (iGremote_ack) == iGsendseq,
pfexit))
return FALSE;
if (*pfexit)
return TRUE;
if (freturncontrol)
{
*pfexit = TRUE;
return TRUE;
}
continue;
}
#if DEBUG > 1
if (FDEBUGGING (DEBUG_PROTO)
|| (FDEBUGGING (DEBUG_ABNORMAL)
&& CONTROL_XXX (ab[IFRAME_CONTROL]) != RR))
ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d",
azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])],
CONTROL_YYY (ab[IFRAME_CONTROL]));
#endif
switch (CONTROL_XXX (ab[IFRAME_CONTROL]))
{
case CLOSE:
if (fLog_sighup)
{
ulog (LOG_ERROR, "Received unexpected CLOSE packet");
(void) fgsend_control (qdaemon, CLOSE, 0);
}
return FALSE;
case RR:
if (! fduprr)
break;
case RJ:
iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]);
iGretransmit_seq = INEXTSEQ (iGremote_ack);
if (iGretransmit_seq == iGsendseq)
iGretransmit_seq = -1;
else
{
char *zpack;
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Remote reject: next %d resending %d",
iGsendseq, iGretransmit_seq);
++cGresent_packets;
if (fduprr)
++cGremote_duprrs;
else
++cGremote_rejects;
++cGerror_level;
if (! fgcheck_errors (qdaemon))
return FALSE;
zpack = zgadjust_ack (iGretransmit_seq);
if (! fsend_data (qdaemon->qconn, zpack,
CFRAMELEN + CPACKLEN (zpack),
TRUE))
return FALSE;
}
break;
case SRJ:
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fgprocess_data: Selective reject of %d",
CONTROL_YYY (ab[IFRAME_CONTROL]));
{
char *zpack;
++cGresent_packets;
++cGremote_rejects;
++cGerror_level;
zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL]));
if (! fsend_data (qdaemon->qconn, zpack,
CFRAMELEN + CPACKLEN (zpack),
TRUE))
return FALSE;
}
break;
case INITC:
case INITB:
case INITA:
break;
}
if (freturncontrol)
{
*pfexit = TRUE;
return TRUE;
}
}
if (pcneed != NULL)
*pcneed = CFRAMELEN;
return TRUE;
}
#ifdef __GNUC__
#ifdef __vax__
#define VAX_ASM 1
#endif
#endif
#if VAX_ASM
#define ROTATE(i) \
asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i))
#else
#define ROTATE(i) i += i + ((i & 0x8000) >> 15)
#endif
#define ITERATION \
\
ROTATE (ichk1); \
\
\
b = BUCHAR (*z++); \
if (b != 0) \
{ \
ichk1 &= 0xffff; \
ichk1 += b; \
ichk2 += ichk1 ^ c; \
if ((ichk1 >> 16) != 0) \
ichk1 ^= ichk2; \
} \
else \
{ \
ichk2 += ichk1 ^ c; \
ichk1 ^= ichk2; \
} \
\
--c
static int
igchecksum (z, c)
register const char *z;
register size_t c;
{
register unsigned long ichk1, ichk2;
ichk1 = 0xffff;
ichk2 = 0;
do
{
register unsigned int b;
ITERATION;
ITERATION;
ITERATION;
ITERATION;
}
while (c > 0);
return ichk1 & 0xffff;
}
static int
igchecksum2 (zfirst, cfirst, zsecond, csecond)
const char *zfirst;
size_t cfirst;
const char *zsecond;
size_t csecond;
{
register unsigned long ichk1, ichk2;
register const char *z;
register size_t c;
z = zfirst;
c = cfirst + csecond;
ichk1 = 0xffff;
ichk2 = 0;
do
{
register unsigned int b;
ITERATION;
--cfirst;
if (cfirst == 0)
z = zsecond;
}
while (c > 0);
return ichk1 & 0xffff;
}