#include "uucp.h"
#if USE_RCS_ID
const char proti_rcsid[] = "$Id: proti.c,v 1.36 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 IHDR_INTRO (0)
#define IHDR_LOCAL (1)
#define IHDR_REMOTE (2)
#define IHDR_CONTENTS1 (3)
#define IHDR_CONTENTS2 (4)
#define IHDR_CHECK (5)
#define IHDRWIN_SET(iseq, ichan) (((iseq) << 3) | (ichan))
#define IHDRWIN_GETSEQ(ival) (((ival) >> 3) & 0x1f)
#define IHDRWIN_GETCHAN(ival) ((ival) & 0x07)
#define IHDRCON_SET1(ttype, fcaller, cbytes) \
(((ttype) << 5) | ((fcaller) ? (1 << 4) : 0) | (((cbytes) >> 8) & 0x0f))
#define IHDRCON_SET2(ttype, fcaller, cbytes) ((cbytes) & 0xff)
#define THDRCON_GETTYPE(i1, i2) (((i1) >> 5) & 0x07)
#define FHDRCON_GETCALLER(i1, i2) (((i1) & (1 << 4)) != 0)
#define CHDRCON_GETBYTES(i1, i2) ((((i1) & 0x0f) << 8) | ((i2) & 0xff))
#define IHDRCHECK_VAL(zhdr) \
((zhdr[IHDR_LOCAL] \
^ zhdr[IHDR_REMOTE] \
^ zhdr[IHDR_CONTENTS1] \
^ zhdr[IHDR_CONTENTS2]) \
& 0xff)
#define CHDRLEN (6)
#define CHDRSKIPLEN (CHDRLEN + (sizeof (long) - CHDRLEN % sizeof (long)))
#define CHDROFFSET (CHDRSKIPLEN - CHDRLEN)
#define CCKSUMLEN (4)
#define ICKSUM_GET(z) \
((((((((unsigned long) ((z)[0] & 0xff)) << 8) \
| (unsigned long) ((z)[1] & 0xff)) << 8) \
| (unsigned long) ((z)[2] & 0xff)) << 8) \
| (unsigned long) ((z)[3] & 0xff))
#define UCKSUM_SET(z, i) \
(void) ((z)[0] = (((i) >> 24) & 0xff), \
(z)[1] = (((i) >> 16) & 0xff), \
(z)[2] = (((i) >> 8) & 0xff), \
(z)[3] = ((i) & 0xff))
#define IINTRO ('\007')
#define DATA (0)
#define SYNC (1)
#define ACK (2)
#define NAK (3)
#define SPOS (4)
#define CLOSE (5)
#define IMAXPACKSIZE ((1 << 12) - 1)
#define IMAXSEQ 32
#define INEXTSEQ(i) (((i) + 1) & (IMAXSEQ - 1))
#define IPREVSEQ(i) (((i) + IMAXSEQ - 1) & (IMAXSEQ - 1))
#define CSEQDIFF(i1, i2) (((i1) + IMAXSEQ - (i2)) & (IMAXSEQ - 1))
#define IMAXICHAN (8)
#define IREQUEST_PACKSIZE (1024)
#define IREQUEST_WINSIZE (16)
#define CSYNC_TIMEOUT (10)
#define CSYNC_RETRIES (6)
#define CTIMEOUT (10)
#define CRETRIES (6)
#define CERRORS (100)
#define CERROR_DECAY (10)
#define ZAVOID "\\021\\023"
static int iIrequest_packsize = IREQUEST_PACKSIZE;
static int iIrequest_winsize = IREQUEST_WINSIZE;
static int iIremote_packsize;
static int iIalc_packsize;
static int iIforced_remote_packsize = 0;
static int iIremote_winsize;
int cIsync_timeout = CSYNC_TIMEOUT;
static int cIsync_retries = CSYNC_RETRIES;
static int cItimeout = CTIMEOUT;
static int cIwindow_timeout = CTIMEOUT;
static int cIretries = CRETRIES;
static int cIerrors = CERRORS;
static int cIerror_decay = CERROR_DECAY;
static int cIack_frequency = 0;
const char *zJavoid_parameter = ZAVOID;
static boolean (*pfIsend) P((struct sconnection *qconn, const char *zsend,
size_t csend, boolean fdoread));
static boolean (*pfIreceive) P((struct sconnection *qconn, size_t cneed,
size_t *pcrec, int ctimeout,
boolean freport));
static int iIsendseq;
static int iIrecseq;
static int iIlocal_ack;
static int iIremote_ack;
static long iIsendpos;
static long iIrecpos;
static boolean fIclosing;
static char *azIsendbuffers[IMAXSEQ];
static char *azIrecbuffers[IMAXSEQ];
static boolean afInaked[IMAXSEQ];
static int cIsyncs;
static long cIsent_packets;
static long cIreceived_packets;
static long cIresent_packets;
static long cIbad_hdr;
static long cIbad_order;
static long cIbad_cksum;
static long cIremote_rejects;
struct uuconf_cmdtab asIproto_params[] =
{
{ "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_packsize,
NULL },
{ "window", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_winsize, NULL },
{ "remote-packet-size", UUCONF_CMDTABTYPE_INT,
(pointer) &iIforced_remote_packsize, NULL },
{ "sync-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_timeout,
NULL },
{ "sync-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_retries,
NULL },
{ "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cItimeout, NULL },
{ "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIretries, NULL },
{ "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cIerrors, NULL },
{ "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cIerror_decay, NULL },
{ "ack-frequency", UUCONF_CMDTABTYPE_INT, (pointer) &cIack_frequency, NULL },
{ "avoid", UUCONF_CMDTABTYPE_STRING, (pointer) &zJavoid_parameter, NULL },
{ NULL, 0, NULL, NULL }
};
static boolean finak P((struct sdaemon *qdaemon, int iseq));
static boolean firesend P((struct sdaemon *qdaemon));
static boolean fiwindow_wait P((struct sdaemon *qdaemon));
static boolean fiwait_for_packet P((struct sdaemon *qdaemon,
int ctimeout, int cretries,
boolean fone, boolean *ftimedout));
static boolean ficheck_errors P((struct sdaemon *qdaemon));
static boolean fiprocess_data P((struct sdaemon *qdaemon,
boolean *pfexit, boolean *pffound,
size_t *pcneed));
static boolean fiprocess_packet P((struct sdaemon *qdaemon,
const char *zhdr,
const char *zfirst, int cfirst,
const char *zsecond, int csecond,
boolean *pfexit));
boolean
fistart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fsend_data, freceive_data);
}
boolean
fijstart (qdaemon, pzlog, imaxpacksize, pfsend, pfreceive)
struct sdaemon *qdaemon;
char **pzlog;
int imaxpacksize;
boolean (*pfsend) P((struct sconnection *qconn, const char *zsend,
size_t csend, boolean fdoread));
boolean (*pfreceive) P((struct sconnection *qconn, size_t cneed,
size_t *pcrec, int ctimeout, boolean freport));
{
char ab[CHDRLEN + 4 + CCKSUMLEN];
unsigned long icksum;
int ctries;
int csyncs;
long ibaud;
*pzlog = NULL;
pfIsend = pfsend;
pfIreceive = pfreceive;
if (iIforced_remote_packsize <= 0
|| iIforced_remote_packsize > imaxpacksize)
iIforced_remote_packsize = 0;
else
iIremote_packsize = iIforced_remote_packsize;
iIalc_packsize = 0;
iIsendseq = 1;
iIrecseq = 0;
iIlocal_ack = 0;
iIremote_ack = 0;
iIsendpos = 0;
iIrecpos = 0;
fIclosing = FALSE;
cIsent_packets = 0;
cIreceived_packets = 0;
cIresent_packets = 0;
cIbad_hdr = 0;
cIbad_order = 0;
cIbad_cksum = 0;
cIremote_rejects = 0;
if (iIrequest_packsize < 0 || iIrequest_packsize > imaxpacksize)
{
ulog (LOG_ERROR, "Illegal protocol '%c' packet size; using %d",
qdaemon->qproto->bname, imaxpacksize);
iIrequest_packsize = imaxpacksize;
}
if (iIrequest_winsize < 0 || iIrequest_winsize > IMAXSEQ / 2)
{
ulog (LOG_ERROR, "Illegal protocol '%c' window size; using %d",
qdaemon->qproto->bname, IREQUEST_WINSIZE);
iIrequest_winsize = IREQUEST_WINSIZE;
}
if (cIack_frequency <= 0 || cIack_frequency >= iIrequest_winsize)
cIack_frequency = iIrequest_winsize / 2;
ab[IHDR_INTRO] = IINTRO;
ab[IHDR_LOCAL] = ab[IHDR_REMOTE] = IHDRWIN_SET (0, 0);
ab[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 4);
ab[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 4);
ab[IHDR_CHECK] = IHDRCHECK_VAL (ab);
ab[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff;
ab[CHDRLEN + 1] = iIrequest_packsize & 0xff;
ab[CHDRLEN + 2] = iIrequest_winsize;
ab[CHDRLEN + 3] = qdaemon->cchans;
icksum = icrc (ab + CHDRLEN, 4, ICRCINIT);
UCKSUM_SET (ab + CHDRLEN + 4, icksum);
csyncs = cIsyncs;
ctries = 0;
while (TRUE)
{
boolean ftimedout;
DEBUG_MESSAGE3 (DEBUG_PROTO,
"fistart: Sending SYNC packsize %d winsize %d channels %d",
iIrequest_packsize, iIrequest_winsize, qdaemon->cchans);
if (! (*pfIsend) (qdaemon->qconn, ab, CHDRLEN + 4 + CCKSUMLEN,
TRUE))
return FALSE;
if (fiwait_for_packet (qdaemon, cIsync_timeout, 0, FALSE,
&ftimedout))
{
if (csyncs != cIsyncs)
break;
}
else
{
if (! ftimedout)
return FALSE;
++ctries;
if (ctries > cIsync_retries)
{
ulog (LOG_ERROR, "Protocol startup failed");
return FALSE;
}
}
}
ibaud = iconn_baud (qdaemon->qconn);
if (ibaud == 0)
cIwindow_timeout = cItimeout;
else
{
cIwindow_timeout = ((5 * iIremote_packsize * iIremote_winsize) / ibaud
+ cItimeout);
}
if (! qdaemon->fcaller)
{
++cItimeout;
++cIwindow_timeout;
}
if (iIremote_packsize > imaxpacksize)
iIremote_packsize = imaxpacksize;
do
{
int iseq;
for (iseq = 0; iseq < IMAXSEQ; iseq++)
{
azIrecbuffers[iseq] = NULL;
afInaked[iseq] = FALSE;
azIsendbuffers[iseq] = (char *) malloc (iIremote_packsize
+ CHDRSKIPLEN
+ CCKSUMLEN);
if (azIsendbuffers[iseq] == NULL)
{
int ifree;
for (ifree = 0; ifree < iseq; ifree++)
free ((pointer) azIsendbuffers[ifree]);
break;
}
}
if (iseq >= IMAXSEQ)
{
*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) iIremote_packsize,
(int) iIremote_winsize, (int) iIrequest_packsize,
(int) iIrequest_winsize);
iIalc_packsize = iIremote_packsize;
return TRUE;
}
iIremote_packsize >>= 1;
}
while (iIremote_packsize > 200);
ulog (LOG_ERROR,
"'%c' protocol startup failed; insufficient memory for packets",
qdaemon->qproto->bname);
return FALSE;
}
boolean
fishutdown (qdaemon)
struct sdaemon *qdaemon;
{
char *z;
size_t clen;
fIclosing = TRUE;
z = zigetspace (qdaemon, &clen) - CHDRLEN;
z[IHDR_INTRO] = IINTRO;
z[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0);
z[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
z[IHDR_CONTENTS1] = IHDRCON_SET1 (CLOSE, qdaemon->fcaller, 0);
z[IHDR_CONTENTS2] = IHDRCON_SET2 (CLOSE, qdaemon->fcaller, 0);
z[IHDR_CHECK] = IHDRCHECK_VAL (z);
DEBUG_MESSAGE0 (DEBUG_PROTO, "fishutdown: Sending CLOSE");
if (! (*pfIsend) (qdaemon->qconn, z, CHDRLEN, FALSE))
return FALSE;
ulog (LOG_NORMAL,
"Protocol '%c' packets: sent %ld, resent %ld, received %ld",
qdaemon->qproto->bname, cIsent_packets, cIresent_packets,
cIreceived_packets);
if (cIbad_hdr != 0
|| cIbad_cksum != 0
|| cIbad_order != 0
|| cIremote_rejects != 0)
ulog (LOG_NORMAL,
"Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
cIbad_hdr, cIbad_cksum, cIbad_order, cIremote_rejects);
iIrequest_packsize = IREQUEST_PACKSIZE;
iIrequest_winsize = IREQUEST_WINSIZE;
iIforced_remote_packsize = 0;
cIsync_timeout = CSYNC_TIMEOUT;
cIsync_retries = CSYNC_RETRIES;
cItimeout = CTIMEOUT;
cIwindow_timeout = CTIMEOUT;
cIretries = CRETRIES;
cIerrors = CERRORS;
cIerror_decay = CERROR_DECAY;
cIack_frequency = 0;
zJavoid_parameter = ZAVOID;
return TRUE;
}
boolean
fisendcmd (qdaemon, z, ilocal, iremote)
struct sdaemon *qdaemon;
const char *z;
int ilocal;
int iremote;
{
size_t clen;
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fisendcmd: Sending command \"%s\"", z);
clen = strlen (z);
while (TRUE)
{
char *zpacket;
size_t csize;
zpacket = zigetspace (qdaemon, &csize);
if (clen < csize)
{
memcpy (zpacket, z, clen + 1);
return fisenddata (qdaemon, zpacket, clen + 1, ilocal, iremote,
(long) -1);
}
memcpy (zpacket, z, csize);
z += csize;
clen -= csize;
if (! fisenddata (qdaemon, zpacket, csize, ilocal, iremote, (long) -1))
return FALSE;
}
}
static boolean
finak (qdaemon, iseq)
struct sdaemon *qdaemon;
int iseq;
{
char abnak[CHDRLEN];
abnak[IHDR_INTRO] = IINTRO;
abnak[IHDR_LOCAL] = IHDRWIN_SET (iseq, 0);
abnak[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
abnak[IHDR_CONTENTS1] = IHDRCON_SET1 (NAK, qdaemon->fcaller, 0);
abnak[IHDR_CONTENTS2] = IHDRCON_SET2 (NAK, qdaemon->fcaller, 0);
abnak[IHDR_CHECK] = IHDRCHECK_VAL (abnak);
afInaked[iseq] = TRUE;
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"finak: Sending NAK %d", iseq);
return (*pfIsend) (qdaemon->qconn, abnak, CHDRLEN, TRUE);
}
static boolean
firesend (qdaemon)
struct sdaemon *qdaemon;
{
int iseq;
char *zhdr;
size_t clen;
iseq = INEXTSEQ (iIremote_ack);
if (iseq == iIsendseq)
{
return TRUE;
}
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"firesend: Resending packet %d", iseq);
zhdr = azIsendbuffers[iseq] + CHDROFFSET;
if (IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]) != iIrecseq)
{
int iremote;
iremote = IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]);
zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr);
iIlocal_ack = iIrecseq;
}
++cIresent_packets;
clen = CHDRCON_GETBYTES (zhdr[IHDR_CONTENTS1],
zhdr[IHDR_CONTENTS2]);
return (*pfIsend) (qdaemon->qconn, zhdr,
CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0),
TRUE);
}
static boolean
fiwindow_wait (qdaemon)
struct sdaemon *qdaemon;
{
while (CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
{
DEBUG_MESSAGE0 (DEBUG_PROTO, "fiwindow_wait: Waiting for ACK");
if (! fiwait_for_packet (qdaemon, cIwindow_timeout, cIretries,
TRUE, (boolean *) NULL))
return FALSE;
}
return TRUE;
}
char *
zigetspace (qdaemon, pclen)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
size_t *pclen;
{
*pclen = iIremote_packsize;
return azIsendbuffers[iIsendseq] + CHDRSKIPLEN;
}
boolean
fisenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
struct sdaemon *qdaemon;
char *zdata;
size_t cdata;
int ilocal;
int iremote;
long ipos;
{
char *zhdr;
unsigned long icksum;
boolean fret;
#if DEBUG > 0
if (ilocal < 0 || ilocal >= IMAXICHAN
|| iremote < 0 || iremote >= IMAXICHAN)
ulog (LOG_FATAL, "fisenddata: ilocal %d iremote %d", ilocal, iremote);
#endif
if (ipos != iIsendpos && ipos != (long) -1)
{
int inext;
char *zspos;
inext = INEXTSEQ (iIsendseq);
zspos = azIsendbuffers[inext];
azIsendbuffers[inext] = zdata - CHDRSKIPLEN;
azIsendbuffers[iIsendseq] = zspos;
zspos += CHDROFFSET;
zspos[IHDR_INTRO] = IINTRO;
zspos[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0);
zspos[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
zspos[IHDR_CONTENTS1] = IHDRCON_SET1 (SPOS, qdaemon->fcaller,
CCKSUMLEN);
zspos[IHDR_CONTENTS2] = IHDRCON_SET2 (SPOS, qdaemon->fcaller,
CCKSUMLEN);
zspos[IHDR_CHECK] = IHDRCHECK_VAL (zspos);
UCKSUM_SET (zspos + CHDRLEN, (unsigned long) ipos);
icksum = icrc (zspos + CHDRLEN, CCKSUMLEN, ICRCINIT);
UCKSUM_SET (zspos + CHDRLEN + CCKSUMLEN, icksum);
if (iIremote_winsize > 0
&& CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
{
if (! fiwindow_wait (qdaemon))
return FALSE;
}
DEBUG_MESSAGE1 (DEBUG_PROTO, "fisenddata: Sending SPOS %ld",
ipos);
if (! (*pfIsend) (qdaemon->qconn, zspos,
CHDRLEN + CCKSUMLEN + CCKSUMLEN, TRUE))
return FALSE;
iIsendseq = INEXTSEQ (iIsendseq);
iIsendpos = ipos;
}
zhdr = zdata - CHDRLEN;
zhdr[IHDR_INTRO] = IINTRO;
zhdr[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, ilocal);
zhdr[IHDR_CONTENTS1] = IHDRCON_SET1 (DATA, qdaemon->fcaller, cdata);
zhdr[IHDR_CONTENTS2] = IHDRCON_SET2 (DATA, qdaemon->fcaller, cdata);
if (cdata > 0)
{
icksum = icrc (zdata, cdata, ICRCINIT);
UCKSUM_SET (zdata + cdata, icksum);
}
if (iIremote_winsize > 0
&& CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
{
if (! fiwindow_wait (qdaemon))
return FALSE;
}
zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
iIlocal_ack = iIrecseq;
zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr);
DEBUG_MESSAGE4 (DEBUG_PROTO,
"fisenddata: Sending packet %d size %d local %d remote %d",
iIsendseq, (int) cdata, ilocal, iremote);
iIsendseq = INEXTSEQ (iIsendseq);
++cIsent_packets;
fret = (*pfIsend) (qdaemon->qconn, zhdr,
cdata + CHDRLEN + (cdata > 0 ? CCKSUMLEN : 0),
TRUE);
iIsendpos += cdata;
if (fret && iPrecstart != iPrecend)
{
boolean fexit;
fret = fiprocess_data (qdaemon, &fexit, (boolean *) NULL,
(size_t *) NULL);
}
return fret;
}
boolean
fiwait (qdaemon)
struct sdaemon *qdaemon;
{
return fiwait_for_packet (qdaemon, cItimeout, cIretries,
FALSE, (boolean *) NULL);
}
static boolean
fiwait_for_packet (qdaemon, ctimeout, cretries, fone, pftimedout)
struct sdaemon *qdaemon;
int ctimeout;
int cretries;
boolean fone;
boolean *pftimedout;
{
int cshort;
int ctimeouts;
if (pftimedout != NULL)
*pftimedout = FALSE;
cshort = 0;
ctimeouts = 0;
while (TRUE)
{
boolean fexit, ffound;
size_t cneed;
size_t crec;
if (! fiprocess_data (qdaemon, &fexit, &ffound, &cneed))
return FALSE;
if (fexit || (fone && ffound))
return TRUE;
if (cneed == 0)
continue;
DEBUG_MESSAGE1 (DEBUG_PROTO, "fiwait_for_packet: Need %d bytes",
(int) cneed);
if (! (*pfIreceive) (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
return FALSE;
if (crec != 0)
{
if (crec >= cneed)
cshort = 0;
else
{
++cshort;
if (cshort > 1)
{
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
cshort = 0;
}
}
}
else
{
int i;
++ctimeouts;
if (ctimeouts > cretries)
{
if (cretries > 0)
ulog (LOG_ERROR, "Timed out waiting for packet");
if (pftimedout != NULL)
*pftimedout = TRUE;
return FALSE;
}
for (i = 0; i < IMAXSEQ; i++)
afInaked[i] = FALSE;
if (! finak (qdaemon, INEXTSEQ (iIrecseq))
|| ! firesend (qdaemon))
return FALSE;
}
}
}
static boolean
ficheck_errors (qdaemon)
struct sdaemon *qdaemon;
{
if (cIerrors < 0)
return TRUE;
if (((cIbad_order + cIbad_hdr + cIbad_cksum + cIremote_rejects)
- (cIreceived_packets / cIerror_decay))
> cIerrors)
{
if (iIrequest_packsize > 400)
{
char absync[CHDRLEN + 3 + CCKSUMLEN];
unsigned long icksum;
iIrequest_packsize /= 2;
absync[IHDR_INTRO] = IINTRO;
absync[IHDR_LOCAL] = IHDRWIN_SET (0, 0);
absync[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
absync[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3);
absync[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3);
absync[IHDR_CHECK] = IHDRCHECK_VAL (absync);
absync[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff;
absync[CHDRLEN + 1] = iIrequest_packsize & 0xff;
absync[CHDRLEN + 2] = iIrequest_winsize;
icksum = icrc (absync + CHDRLEN, 3, ICRCINIT);
UCKSUM_SET (absync + CHDRLEN + 3, icksum);
cIerrors *= 2;
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"ficheck_errors: Sending SYNC packsize %d winsize %d",
iIrequest_packsize, iIrequest_winsize);
return (*pfIsend) (qdaemon->qconn, absync,
CHDRLEN + 3 + CCKSUMLEN, TRUE);
}
ulog (LOG_ERROR, "Too many '%c' protocol errors",
qdaemon->qproto->bname);
return FALSE;
}
return TRUE;
}
static boolean
fiprocess_data (qdaemon, pfexit, pffound, pcneed)
struct sdaemon *qdaemon;
boolean *pfexit;
boolean *pffound;
size_t *pcneed;
{
boolean fbadhdr;
if (pfexit != NULL)
*pfexit = FALSE;
if (pffound != NULL)
*pffound = FALSE;
fbadhdr = FALSE;
while (iPrecstart != iPrecend)
{
char ab[CHDRLEN];
int cfirst, csecond;
char *zfirst, *zsecond;
int i;
int iget;
int ttype;
int iseq;
int csize;
int iack;
if (fIclosing)
{
if (pfexit != NULL)
*pfexit = TRUE;
if (pcneed != NULL)
*pcneed = 0;
return TRUE;
}
if (abPrecbuf[iPrecstart] != IINTRO)
{
char *zintro;
int cintro;
cintro = iPrecend - iPrecstart;
if (cintro < 0)
cintro = CRECBUFLEN - iPrecstart;
zintro = memchr (abPrecbuf + iPrecstart, IINTRO, (size_t) cintro);
if (zintro == NULL)
{
iPrecstart = (iPrecstart + cintro) % CRECBUFLEN;
continue;
}
iPrecstart += zintro - (abPrecbuf + iPrecstart);
}
for (i = 0, iget = iPrecstart;
i < CHDRLEN && iget != iPrecend;
i++, iget = (iget + 1) % CRECBUFLEN)
ab[i] = abPrecbuf[iget];
if (i < CHDRLEN)
{
if (pcneed != NULL)
*pcneed = CHDRLEN - i;
return TRUE;
}
if ((ab[IHDR_CHECK] & 0xff) != IHDRCHECK_VAL (ab)
|| (FHDRCON_GETCALLER (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2])
? qdaemon->fcaller : ! qdaemon->fcaller))
{
if (! fbadhdr)
{
DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_data: Bad header");
++cIbad_hdr;
if (! ficheck_errors (qdaemon))
return FALSE;
fbadhdr = TRUE;
}
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
zfirst = zsecond = NULL;
cfirst = csecond = 0;
ttype = THDRCON_GETTYPE (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]);
if (ttype == DATA || ttype == SPOS || ttype == CLOSE)
iseq = IHDRWIN_GETSEQ (ab[IHDR_LOCAL]);
else
iseq = -1;
csize = CHDRCON_GETBYTES (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]);
if (iseq != -1)
{
if (iIrequest_winsize > 0
&& CSEQDIFF (iseq, iIlocal_ack) > iIrequest_winsize)
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_data: Out of order packet %d (ack %d)",
iseq, iIlocal_ack);
++cIbad_order;
if (! ficheck_errors (qdaemon))
return FALSE;
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
}
if (csize > 0)
{
int cinbuf;
char abcksum[CCKSUMLEN];
unsigned long ickdata;
cinbuf = iPrecend - iPrecstart;
if (cinbuf < 0)
cinbuf += CRECBUFLEN;
if (cinbuf < CHDRLEN + csize + CCKSUMLEN)
{
if (pcneed != NULL)
*pcneed = CHDRLEN + csize + CCKSUMLEN - cinbuf;
return TRUE;
}
if (iPrecend > iPrecstart)
{
cfirst = csize;
zfirst = abPrecbuf + iPrecstart + CHDRLEN;
}
else
{
cfirst = CRECBUFLEN - (iPrecstart + CHDRLEN);
if (cfirst <= 0)
{
zfirst = abPrecbuf - cfirst;
cfirst = csize;
}
else
{
if (cfirst >= csize)
cfirst = csize;
else
{
zsecond = abPrecbuf;
csecond = csize - cfirst;
}
zfirst = abPrecbuf + iPrecstart + CHDRLEN;
}
}
for (i = 0, iget = (iPrecstart + CHDRLEN + csize) % CRECBUFLEN;
i < CCKSUMLEN;
i++, iget = (iget + 1) % CRECBUFLEN)
abcksum[i] = abPrecbuf[iget];
ickdata = icrc (zfirst, (size_t) cfirst, ICRCINIT);
if (csecond > 0)
ickdata = icrc (zsecond, (size_t) csecond, ickdata);
if (ICKSUM_GET (abcksum) != ickdata)
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_data: Bad checksum; data %lu, frame %lu",
ickdata, ICKSUM_GET (abcksum));
++cIbad_cksum;
if (! ficheck_errors (qdaemon))
return FALSE;
if (iseq != -1
&& iseq != iIrecseq
&& (iIrequest_winsize <= 0
|| CSEQDIFF (iseq, iIrecseq) <= iIrequest_winsize)
&& azIrecbuffers[iseq] == NULL)
{
if (! finak (qdaemon, iseq))
return FALSE;
}
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
continue;
}
}
if (csize == 0)
iPrecstart = (iPrecstart + CHDRLEN) % CRECBUFLEN;
else
{
iPrecstart = ((iPrecstart + CHDRLEN + csize + CCKSUMLEN)
% CRECBUFLEN);
++cIreceived_packets;
}
iack = IHDRWIN_GETSEQ (ab[IHDR_REMOTE]);
if (iIremote_winsize > 0
&& iack != iIsendseq
&& CSEQDIFF (iack, iIremote_ack) <= iIremote_winsize
&& CSEQDIFF (iIsendseq, iack) <= iIremote_winsize)
{
if (iack < iIremote_ack)
uwindow_acked (qdaemon, FALSE);
iIremote_ack = iack;
}
if (iseq != -1)
{
if (afInaked[iseq]
&& azIrecbuffers[IPREVSEQ (iseq)] == NULL)
{
for (i = INEXTSEQ (iIrecseq);
i != iseq;
i = INEXTSEQ (i))
afInaked[i] = FALSE;
afInaked[iseq] = FALSE;
}
if (iseq != INEXTSEQ (iIrecseq))
{
if (iseq == iIrecseq
|| (iIrequest_winsize > 0
&& CSEQDIFF (iseq, iIrecseq) > iIrequest_winsize))
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_data: Ignoring out of order packet %d (recseq %d)",
iseq, iIrecseq);
continue;
}
else
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_data: Saving unexpected packet %d (recseq %d)",
iseq, iIrecseq);
if (azIrecbuffers[iseq] == NULL)
{
azIrecbuffers[iseq] = zbufalc ((size_t) (CHDRLEN
+ csize));
memcpy (azIrecbuffers[iseq], ab, CHDRLEN);
if (csize > 0)
{
memcpy (azIrecbuffers[iseq] + CHDRLEN, zfirst,
(size_t) cfirst);
if (csecond > 0)
memcpy (azIrecbuffers[iseq] + CHDRLEN + cfirst,
zsecond, (size_t) csecond);
}
}
}
for (i = INEXTSEQ (iIrecseq);
i != iseq;
i = INEXTSEQ (i))
{
if (! afInaked[i]
&& azIrecbuffers[i] == NULL)
{
if (! finak (qdaemon, i))
return FALSE;
}
}
continue;
}
iIrecseq = iseq;
}
if (pffound != NULL)
*pffound = TRUE;
if (! fiprocess_packet (qdaemon, ab, zfirst, cfirst, zsecond, csecond,
pfexit))
return FALSE;
if (iseq != -1)
{
int inext;
inext = INEXTSEQ (iIrecseq);
while (azIrecbuffers[inext] != NULL)
{
char *z;
int c;
z = azIrecbuffers[inext];
c = CHDRCON_GETBYTES (z[IHDR_CONTENTS1], z[IHDR_CONTENTS2]);
iIrecseq = inext;
if (! fiprocess_packet (qdaemon, z, z + CHDRLEN, c,
(char *) NULL, 0, pfexit))
return FALSE;
ubuffree (azIrecbuffers[inext]);
azIrecbuffers[inext] = NULL;
inext = INEXTSEQ (inext);
}
}
if (iIrequest_winsize > 0
&& CSEQDIFF (iIrecseq, iIlocal_ack) >= cIack_frequency)
{
char aback[CHDRLEN];
aback[IHDR_INTRO] = IINTRO;
aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0);
aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0);
aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0);
aback[IHDR_CHECK] = IHDRCHECK_VAL (aback);
DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_data: Sending ACK %d",
iIrecseq);
if (! (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE))
return FALSE;
}
}
if (pcneed != NULL)
*pcneed = CHDRLEN;
return TRUE;
}
static boolean
fiprocess_packet (qdaemon, zhdr, zfirst, cfirst, zsecond, csecond, pfexit)
struct sdaemon *qdaemon;
const char *zhdr;
const char *zfirst;
int cfirst;
const char *zsecond;
int csecond;
boolean *pfexit;
{
int ttype;
ttype = THDRCON_GETTYPE (zhdr[IHDR_CONTENTS1], zhdr[IHDR_CONTENTS2]);
switch (ttype)
{
case DATA:
{
int iseq;
boolean fret;
iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]);
DEBUG_MESSAGE4 (DEBUG_PROTO,
"fiprocess_packet: Got DATA packet %d size %d local %d remote %d",
iseq, cfirst + csecond,
IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]),
IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL]));
fret = fgot_data (qdaemon, zfirst, (size_t) cfirst,
zsecond, (size_t) csecond,
IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]),
IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL]),
iIrecpos,
INEXTSEQ (iIremote_ack) == iIsendseq,
pfexit);
iIrecpos += cfirst + csecond;
return fret;
}
case SYNC:
{
int ipack, iwin, cchans;
if (cfirst + csecond < 3)
{
ulog (LOG_ERROR, "Bad SYNC packet");
return FALSE;
}
ipack = (zfirst[0] & 0xff) << 8;
if (cfirst > 1)
ipack |= zfirst[1] & 0xff;
else
ipack |= zsecond[0];
if (cfirst > 2)
iwin = zfirst[2];
else
iwin = zsecond[2 - cfirst];
if (cfirst + csecond <= 3)
cchans = 0;
else
{
if (cfirst > 3)
cchans = zfirst[3];
else
cchans = zsecond[3 - cfirst];
if (cchans > 0 && cchans < 8)
qdaemon->cchans = cchans;
}
DEBUG_MESSAGE3 (DEBUG_PROTO,
"fiprocess_packet: Got SYNC packsize %d winsize %d channels %d",
ipack, iwin, cchans);
if (iIforced_remote_packsize == 0
&& (iIalc_packsize == 0
|| ipack <= iIalc_packsize))
iIremote_packsize = ipack;
iIremote_winsize = iwin;
++cIsyncs;
*pfexit = TRUE;
return TRUE;
}
case ACK:
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fiprocess_packet: Got ACK %d",
IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]));
return TRUE;
case NAK:
{
int iseq;
char *zsend;
size_t clen;
++cIremote_rejects;
if (! ficheck_errors (qdaemon))
return FALSE;
iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]);
if (iseq == iIsendseq &&
INEXTSEQ (iIremote_ack) == iIsendseq)
{
char aback[CHDRLEN];
aback[IHDR_INTRO] = IINTRO;
aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0);
aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
iIlocal_ack = iIrecseq;
aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0);
aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0);
aback[IHDR_CHECK] = IHDRCHECK_VAL (aback);
DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Sending ACK %d",
iIrecseq);
return (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE);
}
else
{
if (iseq == iIsendseq
|| (iIremote_winsize > 0
&& (CSEQDIFF (iseq, iIremote_ack) > iIremote_winsize
|| CSEQDIFF (iIsendseq, iseq) > iIremote_winsize)))
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_packet: Ignoring out of order NAK %d (sendseq %d)",
iseq, iIsendseq);
return TRUE;
}
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fiprocess_packet: Got NAK %d; resending packet",
iseq);
zsend = azIsendbuffers[iseq] + CHDROFFSET;
if (IHDRWIN_GETSEQ (zsend[IHDR_REMOTE]) != iIrecseq)
{
int iremote;
iremote = IHDRWIN_GETCHAN (zsend[IHDR_REMOTE]);
zsend[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
zsend[IHDR_CHECK] = IHDRCHECK_VAL (zsend);
iIlocal_ack = iIrecseq;
}
++cIresent_packets;
clen = CHDRCON_GETBYTES (zsend[IHDR_CONTENTS1],
zsend[IHDR_CONTENTS2]);
return (*pfIsend) (qdaemon->qconn, zsend,
CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0),
TRUE);
}
}
case SPOS:
{
char abpos[CCKSUMLEN];
const char *zpos;
if (cfirst >= CCKSUMLEN)
zpos = zfirst;
else
{
memcpy (abpos, zfirst, (size_t) cfirst);
memcpy (abpos + cfirst, zsecond, (size_t) (CCKSUMLEN - cfirst));
zpos = abpos;
}
iIrecpos = (long) ICKSUM_GET (zpos);
DEBUG_MESSAGE1 (DEBUG_PROTO,
"fiprocess_packet: Got SPOS %ld", iIrecpos);
return TRUE;
}
case CLOSE:
{
boolean fexpected;
fexpected = ! fLog_sighup || fIclosing;
if (! fexpected)
ulog (LOG_ERROR, "Received unexpected CLOSE packet");
else
DEBUG_MESSAGE0 (DEBUG_PROTO, "fiprocess_packet: Got CLOSE packet");
fIclosing = TRUE;
*pfexit = TRUE;
return fexpected;
}
default:
DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got packet type %d",
ttype);
return TRUE;
}
}