#include "uucp.h"
#if USE_RCS_ID
const char proty_id[] = "$Id: proty.c,v 1.9 2003/05/29 06:00:49 ian Rel $";
#endif
#include "uudefs.h"
#include "uuconf.h"
#include "conn.h"
#include "trans.h"
#include "system.h"
#include "prot.h"
#define FROMLITTLE(p) (((p)[0] & 0xff) + (((p)[1] & 0xff) << 8))
#define TOLITTLE(p, i) ((p)[0] = (i) & 0xff, (p)[1] = ((i) >> 8) & 0xff)
#define CYBUFSIZE (1024)
#define IYPACKSIZE (1024)
#define CYFRAMELEN (6)
#define YFRAME_SEQ_OFF (0)
#define YFRAME_LEN_OFF (2)
#define YFRAME_CTL_OFF (2)
#define YFRAME_CHK_OFF (4)
#define YFRAME_SEQ (0)
#define YFRAME_LEN (1)
#define YFRAME_CTL (1)
#define YFRAME_CHK (2)
#define CYTIMEOUT (60)
#define YPKT_ACK (0xFFFE)
#define YPKT_ERR (0xFFFD)
#define YPKT_BAD (0xFFFC)
#define Y_VERSION (1)
#define Y_INIT_HDR_LEN (4)
#define Y_INIT_HDR_VERSION_OFF (0)
#define Y_INIT_HDR_PKTSIZE_OFF (1)
#define Y_INIT_HDR_FLAGS_OFF (2)
#define MIN_Y_SYNC (4)
#define MAX_Y_SYNC IYPACKSIZE
static size_t iYlocal_packsize = IYPACKSIZE;
static size_t iYremote_packsize = IYPACKSIZE;
static unsigned short iYlocal_pktnum;
static unsigned short iYremote_pktnum;
static int cYtimeout = CYTIMEOUT;
static char *zYbuf;
struct uuconf_cmdtab asYproto_params[] =
{
{ "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cYtimeout, NULL },
{ "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iYlocal_packsize, NULL },
{ NULL, 0, NULL, NULL }
};
static boolean fyxchg_syncs P((struct sdaemon *qdaemon));
static boolean fywait_for_packet P((struct sdaemon *qdaemon,
boolean *pfexit));
static unsigned short iychecksum P((const char *z, size_t c));
static unsigned short iychecksum2 P((const char *zfirst, size_t cfirst,
const char *zsecond, size_t csecond));
static boolean fywait_for_header P((struct sdaemon *qdaemon,
unsigned short header[3], int timeout));
static boolean fysend_pkt P((struct sdaemon *qdaemon,
const void *zdata, size_t cdata));
static boolean fysend_control P((struct sdaemon *qdaemon,
int itype));
static boolean fyread_data P((struct sdaemon *qdaemon, size_t clen,
int timeout));
static boolean
fyxchg_syncs (qdaemon)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
{
char inithdr[Y_INIT_HDR_LEN];
unsigned short header[3];
unsigned short ichk;
size_t clen, cfirst;
int rpktsize;
inithdr[Y_INIT_HDR_VERSION_OFF] = Y_VERSION;
inithdr[Y_INIT_HDR_PKTSIZE_OFF] = iYlocal_packsize >> 8;
TOLITTLE (inithdr + Y_INIT_HDR_FLAGS_OFF, 0);
if (! fysend_pkt (qdaemon, inithdr, Y_INIT_HDR_LEN))
return FALSE;
if (! fywait_for_header (qdaemon, header, cYtimeout))
return FALSE;
DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fyxchg_syncs: Got sync header");
clen = header[YFRAME_LEN];
if (clen < MIN_Y_SYNC || clen > MAX_Y_SYNC)
{
ulog (LOG_ERROR, "Bad 'y' protocol sync packet length");
return FALSE;
}
if (! fyread_data (qdaemon, clen, cYtimeout))
return FALSE;
cfirst = CRECBUFLEN - iPrecstart;
ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst,
abPrecbuf, clen - cfirst);
rpktsize = BUCHAR (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN]);
if (rpktsize == 0 || header[YFRAME_CHK] != ichk)
{
ulog (LOG_ERROR, "Bad 'y' protocol sync packet");
return FALSE;
}
iYremote_packsize = rpktsize << 8;
if (iYremote_packsize > iYlocal_packsize)
iYremote_packsize = iYlocal_packsize;
iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
return TRUE;
}
boolean
fystart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
*pzlog = NULL;
if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
return FALSE;
iYlocal_pktnum = iYremote_pktnum = 0;
iYlocal_packsize &= ~0xff;
if (iYlocal_packsize < 256 || iYlocal_packsize > (16*1024))
iYlocal_packsize = IYPACKSIZE;
if (! fyxchg_syncs (qdaemon))
{
cYtimeout = CYTIMEOUT;
iYlocal_packsize = IYPACKSIZE;
return FALSE;
}
zYbuf = (char *) xmalloc (CYBUFSIZE + CYFRAMELEN);
return TRUE;
}
boolean
fyshutdown (qdaemon)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
{
xfree ((pointer) zYbuf);
zYbuf = NULL;
cYtimeout = CYTIMEOUT;
iYlocal_packsize = IYPACKSIZE;
return TRUE;
}
boolean
fysendcmd (qdaemon, z, ilocal, iremote)
struct sdaemon *qdaemon;
const char *z;
int ilocal ATTRIBUTE_UNUSED;
int iremote ATTRIBUTE_UNUSED;
{
size_t clen;
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fysendcmd: Sending command \"%s\"", z);
clen = strlen (z) + 1;
while (clen > 0)
{
size_t csize;
csize = clen;
if (csize > iYremote_packsize)
csize = iYremote_packsize;
if (! fysend_pkt (qdaemon, z, csize))
return FALSE;
z += csize;
clen -= csize;
}
return TRUE;
}
char *
zygetspace (qdaemon, pclen)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
size_t *pclen;
{
*pclen = iYremote_packsize;
return zYbuf + CYFRAMELEN;
}
boolean
fysenddata (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;
{
#if DEBUG > 0
if (cdata > iYremote_packsize)
ulog (LOG_FATAL, "fysend_packet: Packet size too large");
#endif
TOLITTLE (zYbuf + YFRAME_SEQ_OFF, iYlocal_pktnum);
++iYlocal_pktnum;
TOLITTLE (zYbuf + YFRAME_LEN_OFF, cdata);
TOLITTLE (zYbuf + YFRAME_CHK_OFF, iychecksum (zdata, cdata));
return fsend_data (qdaemon->qconn, zYbuf, cdata + CYFRAMELEN, FALSE);
}
boolean
fywait (qdaemon)
struct sdaemon *qdaemon;
{
boolean fexit = FALSE;
while (! fexit)
{
if (! fywait_for_packet (qdaemon, &fexit))
return FALSE;
}
return TRUE;
}
boolean
fyfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
struct sdaemon *qdaemon;
struct stransfer *qtrans ATTRIBUTE_UNUSED;
boolean fstart;
boolean fsend;
long cbytes ATTRIBUTE_UNUSED;
boolean *pfhandled;
{
unsigned short header[3];
*pfhandled = FALSE;
if (! fstart)
{
if (fsend)
{
if (! fywait_for_header (qdaemon, header, cYtimeout * 2))
return FALSE;
if (header[YFRAME_CTL] != (unsigned short) YPKT_ACK)
{
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fyfile: Error from remote: 0x%04X", header[1]);
ulog (LOG_ERROR, "Received 'y' protocol error from remote");
return FALSE;
}
}
else
{
return fysend_control (qdaemon, YPKT_ACK);
}
}
return TRUE;
}
static boolean
fysend_control (qdaemon, itype)
struct sdaemon *qdaemon;
int itype;
{
char header[CYFRAMELEN];
TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum);
iYlocal_pktnum++;
TOLITTLE (header + YFRAME_CTL_OFF, itype);
TOLITTLE (header + YFRAME_CHK_OFF, 0);
return fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE);
}
static boolean
fysend_pkt (qdaemon, zdata, cdata)
struct sdaemon *qdaemon;
const void *zdata;
size_t cdata;
{
char header[CYFRAMELEN];
TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum);
iYlocal_pktnum++;
TOLITTLE (header + YFRAME_LEN_OFF, cdata);
TOLITTLE (header + YFRAME_CHK_OFF, iychecksum (zdata, cdata));
if (! fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE))
return FALSE;
return fsend_data (qdaemon->qconn, zdata, cdata, FALSE);
}
static boolean
fyread_data (qdaemon, clen, timeout)
struct sdaemon *qdaemon;
size_t clen;
int timeout;
{
int cinbuf;
size_t crec;
cinbuf = iPrecend - iPrecstart;
if (cinbuf < 0)
cinbuf += CRECBUFLEN;
if ((size_t) cinbuf < clen)
{
if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
timeout, TRUE))
return FALSE;
cinbuf += crec;
if ((size_t) cinbuf < clen)
{
if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
timeout, TRUE))
return FALSE;
}
cinbuf += crec;
if ((size_t) cinbuf < clen)
{
ulog (LOG_ERROR, "Timed out waiting for data");
return FALSE;
}
}
return TRUE;
}
static boolean
fywait_for_header (qdaemon, header, timeout)
struct sdaemon *qdaemon;
unsigned short header[3];
int timeout;
{
if (! fyread_data (qdaemon, CYFRAMELEN, timeout))
return FALSE;
if (iPrecstart <= (CRECBUFLEN - CYFRAMELEN))
{
header[0] = FROMLITTLE (abPrecbuf + iPrecstart);
header[1] = FROMLITTLE (abPrecbuf + iPrecstart + 2);
header[2] = FROMLITTLE (abPrecbuf + iPrecstart + 4);
}
else
{
register int i, j;
for (i = j = 0; j < CYFRAMELEN; i++, j += 2)
{
header[i] =
(((abPrecbuf[(iPrecstart + j + 1) % CRECBUFLEN] & 0xff) << 8)
+ (abPrecbuf[(iPrecstart + j) % CRECBUFLEN] & 0xff));
}
}
iPrecstart = (iPrecstart + CYFRAMELEN) % CRECBUFLEN;
DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO,
"fywait_for_header: Got header: 0x%04X, 0x%04X, 0x%04X",
header[0], header[1], header[2]);
if (header[YFRAME_SEQ] != iYremote_pktnum++)
{
ulog (LOG_ERROR, "Incorrect 'y' packet sequence");
fysend_control (qdaemon, YPKT_BAD);
return FALSE;
}
return TRUE;
}
static boolean
fywait_for_packet (qdaemon, pfexit)
struct sdaemon *qdaemon;
boolean *pfexit;
{
unsigned short header[3], ichk;
size_t clen, cfirst;
if (! fywait_for_header (qdaemon, header, cYtimeout))
return FALSE;
clen = header[YFRAME_LEN];
if (clen == 0 && pfexit != NULL)
{
return fgot_data (qdaemon, abPrecbuf, 0, abPrecbuf, 0,
-1, -1, (long) -1, TRUE, pfexit);
}
if (clen & 0x8000)
{
DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fywait_for_packet: Error from remote: 0x%04X",
header[YFRAME_CTL]);
ulog (LOG_ERROR, "Remote error packet");
return FALSE;
}
if (clen > iYlocal_packsize)
{
ulog (LOG_ERROR, "Packet too large");
return FALSE;
}
if (! fyread_data (qdaemon, clen, cYtimeout))
return FALSE;
cfirst = CRECBUFLEN - iPrecstart;
if (cfirst > clen)
cfirst = clen;
if (cfirst == clen)
ichk = iychecksum (abPrecbuf + iPrecstart, clen);
else
ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst,
abPrecbuf, clen - cfirst);
if (header[YFRAME_CHK] != ichk)
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"fywait_for_packet: Bad checksum 0x%x != 0x%x",
header[YFRAME_CHK], ichk);
fysend_control (qdaemon, YPKT_ERR);
ulog (LOG_ERROR, "Checksum error");
return FALSE;
}
if (pfexit != NULL
&& ! fgot_data (qdaemon, abPrecbuf + iPrecstart, cfirst,
abPrecbuf, clen - cfirst,
-1, -1, (long) -1, TRUE, pfexit))
return FALSE;
iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
return TRUE;
}
#ifdef __GNUC__
#ifdef __i386__
#define I386_ASM
#endif
#endif
#ifdef I386_ASM
#define ROTATE(i) \
asm ("rolw $1,%0" : "=g" (i) : "g" (i))
#else
#define ROTATE(i) i += i + ((i & 0x8000) >> 15)
#endif
static unsigned short
iychecksum (z, c)
register const char *z;
register size_t c;
{
register unsigned short ichk;
ichk = 0xffff;
while (c-- > 0)
{
ROTATE (ichk);
ichk += BUCHAR (*z++);
}
return ichk;
}
static unsigned short
iychecksum2 (zfirst, cfirst, zsecond, csecond)
const char *zfirst;
size_t cfirst;
const char *zsecond;
size_t csecond;
{
register unsigned short ichk;
register const char *z;
register size_t c;
z = zfirst;
c = cfirst + csecond;
ichk = 0xffff;
while (c-- > 0)
{
ROTATE (ichk);
ichk += BUCHAR (*z++);
if (--cfirst == 0)
z = zsecond;
}
return ichk;
}