674 lines
15 KiB
C
674 lines
15 KiB
C
/* protf.c
|
||
The 'f' protocol.
|
||
|
||
Copyright (C) 1991, 1992 Ian Lance Taylor
|
||
|
||
This file is part of the Taylor UUCP package.
|
||
|
||
This program is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU General Public License as
|
||
published by the Free Software Foundation; either version 2 of the
|
||
License, or (at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
||
The author of the program may be contacted at ian@airs.com or
|
||
c/o AIRS, P.O. Box 520, Waltham, MA 02254.
|
||
|
||
$Log: protf.c,v $
|
||
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
|
||
initial import of 386bsd-0.1 sources
|
||
|
||
Revision 1.15 1992/03/30 04:49:10 ian
|
||
Niels Baggesen: added debugging types abnormal and uucp-proto
|
||
|
||
Revision 1.14 1992/03/17 01:03:03 ian
|
||
Miscellaneous cleanup
|
||
|
||
Revision 1.13 1992/03/13 22:59:25 ian
|
||
Have breceive_char go through freceive_data
|
||
|
||
Revision 1.12 1992/03/12 19:56:10 ian
|
||
Debugging based on types rather than number
|
||
|
||
Revision 1.11 1992/02/08 03:54:18 ian
|
||
Include <string.h> only in <uucp.h>, added 1992 copyright
|
||
|
||
Revision 1.10 1992/01/16 18:16:58 ian
|
||
Niels Baggesen: add some debugging messages
|
||
|
||
Revision 1.9 1992/01/14 04:35:23 ian
|
||
Chip Salzenberg: implement this patch correctly
|
||
|
||
Revision 1.8 1992/01/14 04:21:59 ian
|
||
Chip Salzenberg: avoid use before set warning
|
||
|
||
Revision 1.7 1991/12/31 19:34:19 ian
|
||
Added number of bytes to pffile protocol entry point
|
||
|
||
Revision 1.6 1991/12/20 03:02:01 ian
|
||
Oleg Tabarovsky: added statistical messages to 'g' and 'f' protocols
|
||
|
||
Revision 1.5 1991/12/20 00:01:54 ian
|
||
Franc,ois Pinard: don't crash 'f' protocol because of an illegal byte
|
||
|
||
Revision 1.4 1991/11/16 00:31:01 ian
|
||
Increased default 't' and 'f' protocol timeouts
|
||
|
||
Revision 1.3 1991/11/15 23:32:15 ian
|
||
Don't use 1 second timeouts--loses data on System V
|
||
|
||
Revision 1.2 1991/11/15 21:00:59 ian
|
||
Efficiency hacks for 'f' and 't' protocols
|
||
|
||
Revision 1.1 1991/11/11 04:21:16 ian
|
||
Initial revision
|
||
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
char protf_rcsid[] = "$Id: protf.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
||
#endif
|
||
|
||
#include <ctype.h>
|
||
#include <errno.h>
|
||
|
||
#include "prot.h"
|
||
#include "port.h"
|
||
#include "system.h"
|
||
|
||
/* This implementation is based on code by Piet Beertema, CWI,
|
||
Amsterdam, Sep 1984.
|
||
|
||
This code implements the 'f' protocol, which requires a
|
||
flow-controlled error-free seven-bit data path. It does check for
|
||
errors, but only at the end of each file transmission, so a noisy
|
||
line without error correcting modems will be unusable.
|
||
|
||
The conversion to seven bit data is done as follows, where b
|
||
represents the character to convert:
|
||
|
||
0 <= b <= 037: 0172, b + 0100 (0100 to 0137)
|
||
040 <= b <= 0171: b ( 040 to 0171)
|
||
0172 <= b <= 0177: 0173, b - 0100 ( 072 to 077)
|
||
0200 <= b <= 0237: 0174, b - 0100 (0100 to 0137)
|
||
0240 <= b <= 0371: 0175, b - 0200 ( 040 to 0171)
|
||
0372 <= b <= 0377: 0176, b - 0300 ( 072 to 077)
|
||
|
||
This causes all output bytes to be in the range 040 to 0176; these
|
||
are the printable ASCII characters. */
|
||
|
||
/* Internal functions. */
|
||
static boolean ffprocess_data P((boolean *pfexit, int *pcneed));
|
||
|
||
/* The size of the buffer we allocate to store outgoing data in. */
|
||
#define CFBUFSIZE (256)
|
||
|
||
/* The timeout to wait for data to arrive before giving up. */
|
||
static int cFtimeout = 120;
|
||
|
||
/* The maximum number of retries. */
|
||
static int cFmaxretries = 2;
|
||
|
||
/* The buffer we allocate for outgoing data. */
|
||
static char *zFbuf;
|
||
|
||
/* TRUE if we are receiving a file rather than a command. */
|
||
static boolean fFfile;
|
||
|
||
/* The checksum so far. */
|
||
static unsigned int iFcheck;
|
||
|
||
/* The last special byte (0172 to 0176) or 0 if none. */
|
||
static char bFspecial;
|
||
|
||
/* The number of times we have retried this file. */
|
||
static int cFretries;
|
||
|
||
struct scmdtab asFproto_params[] =
|
||
{
|
||
{ "timeout", CMDTABTYPE_INT, (pointer) &cFtimeout, NULL },
|
||
{ "retries", CMDTABTYPE_INT, (pointer) &cFmaxretries, NULL },
|
||
{ NULL, 0, NULL, NULL }
|
||
};
|
||
|
||
/* Statistics. */
|
||
|
||
/* The number of data bytes sent in files. */
|
||
static long cFsent_data;
|
||
|
||
/* The number of actual bytes sent in files. */
|
||
static long cFsent_bytes;
|
||
|
||
/* The number of data bytes received in files. */
|
||
static long cFrec_data;
|
||
|
||
/* The number of actual bytes received in files. */
|
||
static long cFrec_bytes;
|
||
|
||
/* The number of file retries when sending. */
|
||
static long cFsend_retries;
|
||
|
||
/* The number of file retries when receiving. */
|
||
static long cFrec_retries;
|
||
|
||
/* Start the protocol. */
|
||
|
||
/*ARGSUSED*/
|
||
boolean
|
||
ffstart (fmaster)
|
||
boolean fmaster;
|
||
{
|
||
cFsent_data = 0;
|
||
cFsent_bytes = 0;
|
||
cFrec_data = 0;
|
||
cFrec_bytes = 0;
|
||
cFsend_retries = 0;
|
||
cFrec_retries = 0;
|
||
|
||
/* Allow XON/XOFF to work. */
|
||
if (! fport_set (PORTSETTING_SEVEN))
|
||
return FALSE;
|
||
|
||
/* We sleep to allow the other side to reset the terminal; this is
|
||
what Mr. Beertema's code does. */
|
||
usysdep_sleep (2);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Shutdown the protocol. */
|
||
|
||
boolean
|
||
ffshutdown ()
|
||
{
|
||
xfree ((pointer) zFbuf);
|
||
zFbuf = NULL;
|
||
ulog (LOG_NORMAL,
|
||
"Protocol 'f': sent %ld bytes for %ld, received %ld bytes for %ld",
|
||
cFsent_bytes, cFsent_data, cFrec_bytes, cFrec_data);
|
||
if (cFsend_retries != 0 || cFrec_retries != 0)
|
||
ulog (LOG_NORMAL, "Protocol 'f' file retries: %ld sending, %ld receiving",
|
||
cFsend_retries, cFrec_retries);
|
||
return TRUE;
|
||
}
|
||
|
||
/* Send a command string. We just send the string followed by a carriage
|
||
return. */
|
||
|
||
boolean
|
||
ffsendcmd (z)
|
||
const char *z;
|
||
{
|
||
int clen;
|
||
char *zalc;
|
||
|
||
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ffsendcmd: Sending command \"%s\"", z);
|
||
|
||
clen = strlen (z);
|
||
zalc = (char *) alloca (clen + 2);
|
||
sprintf (zalc, "%s\r", z);
|
||
return fsend_data (zalc, clen + 1, TRUE);
|
||
}
|
||
|
||
/* Get space to be filled with data. We allocate the space from the
|
||
heap. */
|
||
|
||
char *
|
||
zfgetspace (pclen)
|
||
int *pclen;
|
||
{
|
||
*pclen = CFBUFSIZE;
|
||
if (zFbuf == NULL)
|
||
zFbuf = (char *) xmalloc (CFBUFSIZE);
|
||
return zFbuf;
|
||
}
|
||
|
||
/* Send out a data packet. We have to encode the data into seven bits
|
||
and accumulate a checksum. */
|
||
|
||
boolean
|
||
ffsenddata (zdata, cdata)
|
||
char *zdata;
|
||
int cdata;
|
||
{
|
||
char ab[CFBUFSIZE * 2];
|
||
char *ze;
|
||
register unsigned int itmpchk;
|
||
|
||
cFsent_data += cdata;
|
||
|
||
ze = ab;
|
||
itmpchk = iFcheck;
|
||
while (cdata-- > 0)
|
||
{
|
||
register int b;
|
||
|
||
/* Rotate the checksum left. */
|
||
if ((itmpchk & 0x8000) == 0)
|
||
itmpchk <<= 1;
|
||
else
|
||
{
|
||
itmpchk <<= 1;
|
||
++itmpchk;
|
||
}
|
||
|
||
/* Add the next byte into the checksum. */
|
||
b = *zdata++ & 0xff;
|
||
itmpchk += b;
|
||
|
||
/* Encode the byte. */
|
||
if (b <= 0177)
|
||
{
|
||
if (b <= 037)
|
||
{
|
||
*ze++ = '\172';
|
||
*ze++ = (char) (b + 0100);
|
||
}
|
||
else if (b <= 0171)
|
||
*ze++ = (char) b;
|
||
else
|
||
{
|
||
*ze++ = '\173';
|
||
*ze++ = (char) (b - 0100);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (b <= 0237)
|
||
{
|
||
*ze++ = '\174';
|
||
*ze++ = (char) (b - 0100);
|
||
}
|
||
else if (b <= 0371)
|
||
{
|
||
*ze++ = '\175';
|
||
*ze++ = (char) (b - 0200);
|
||
}
|
||
else
|
||
{
|
||
*ze++ = '\176';
|
||
*ze++ = (char) (b - 0300);
|
||
}
|
||
}
|
||
}
|
||
|
||
iFcheck = itmpchk;
|
||
|
||
cFsent_bytes += ze - ab;
|
||
|
||
/* Passing FALSE tells fsend_data not to bother looking for incoming
|
||
information, since we really don't expect any. */
|
||
return fsend_data (ab, ze - ab, FALSE);
|
||
}
|
||
|
||
/* Process any data in the receive buffer. */
|
||
|
||
boolean
|
||
ffprocess (pfexit)
|
||
boolean *pfexit;
|
||
{
|
||
return ffprocess_data (pfexit, (int *) NULL);
|
||
}
|
||
|
||
/* Process data and return the amount of data we are looking for in
|
||
*pcneed. The 'f' protocol doesn't really reveal this, but when
|
||
transferring file we know that we need at least seven characters
|
||
for the checksum. */
|
||
|
||
static boolean
|
||
ffprocess_data (pfexit, pcneed)
|
||
boolean *pfexit;
|
||
int *pcneed;
|
||
{
|
||
int i;
|
||
register unsigned int itmpchk;
|
||
|
||
if (pcneed != NULL)
|
||
*pcneed = 1;
|
||
|
||
if (! fFfile)
|
||
{
|
||
/* A command continues until a '\r' character, which we turn
|
||
into '\0' before calling fgot_data. */
|
||
|
||
while (iPrecstart != iPrecend)
|
||
{
|
||
for (i = iPrecstart; i < CRECBUFLEN && i != iPrecend; i++)
|
||
{
|
||
if (abPrecbuf[i] == '\r')
|
||
{
|
||
int istart;
|
||
|
||
abPrecbuf[i] = '\0';
|
||
istart = iPrecstart;
|
||
iPrecstart = (i + 1) % CRECBUFLEN;
|
||
return fgot_data (abPrecbuf + istart, i - istart + 1,
|
||
TRUE, FALSE, pfexit);
|
||
}
|
||
}
|
||
|
||
if (! fgot_data (abPrecbuf + iPrecstart, i - iPrecstart,
|
||
TRUE, FALSE, pfexit))
|
||
return FALSE;
|
||
|
||
iPrecstart = i % CRECBUFLEN;
|
||
}
|
||
|
||
*pfexit = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
/* Here the data is destined for a file, and we must decode it. */
|
||
|
||
itmpchk = iFcheck;
|
||
|
||
while (iPrecstart != iPrecend)
|
||
{
|
||
char *zstart, *zto, *zfrom;
|
||
int c;
|
||
|
||
zto = zfrom = zstart = abPrecbuf + iPrecstart;
|
||
|
||
c = iPrecend - iPrecstart;
|
||
if (c < 0)
|
||
c = CRECBUFLEN - iPrecstart;
|
||
|
||
while (c-- != 0)
|
||
{
|
||
int b;
|
||
|
||
b = *zfrom++ & 0xff;
|
||
if (b < 040 || b > 0176)
|
||
{
|
||
ulog (LOG_ERROR, "Illegal byte %d", b);
|
||
continue;
|
||
}
|
||
|
||
/* Characters >= 0172 are always special characters. The
|
||
only legal pair of consecutive special characters
|
||
are 0176 0176 which immediately precede the four
|
||
digit checksum. */
|
||
|
||
if (b >= 0172)
|
||
{
|
||
if (bFspecial != 0)
|
||
{
|
||
if (bFspecial != 0176 || b != 0176)
|
||
{
|
||
ulog (LOG_ERROR, "Illegal bytes %d %d",
|
||
bFspecial, b);
|
||
bFspecial = 0;
|
||
continue;
|
||
}
|
||
|
||
/* Pass any initial data. */
|
||
if (zto != zstart)
|
||
{
|
||
/* Don't count the checksum in the received bytes. */
|
||
cFrec_bytes += zfrom - zstart - 2;
|
||
cFrec_data += zto - zstart;
|
||
if (! fgot_data (zstart, zto - zstart, FALSE,
|
||
TRUE, pfexit))
|
||
return FALSE;
|
||
}
|
||
|
||
/* The next characters we want to read are the
|
||
checksum, so skip the second 0176. */
|
||
iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN;
|
||
|
||
iFcheck = itmpchk;
|
||
|
||
/* Tell fgot_data that we've read the entire file by
|
||
passing 0 length data. This will set *pfexit to
|
||
TRUE and call fffile to verify the checksum. */
|
||
return fgot_data ((char *) NULL, 0, FALSE, TRUE, pfexit);
|
||
}
|
||
|
||
/* Here we have encountered a special character that
|
||
does not follow another special character. */
|
||
bFspecial = (char) b;
|
||
}
|
||
else
|
||
{
|
||
int bnext;
|
||
|
||
/* Here we have encountered a nonspecial character. */
|
||
|
||
switch (bFspecial)
|
||
{
|
||
default:
|
||
bnext = b;
|
||
break;
|
||
case 0172:
|
||
bnext = b - 0100;
|
||
break;
|
||
case 0173:
|
||
case 0174:
|
||
bnext = b + 0100;
|
||
break;
|
||
case 0175:
|
||
bnext = b + 0200;
|
||
break;
|
||
case 0176:
|
||
bnext = b + 0300;
|
||
break;
|
||
}
|
||
|
||
*zto++ = (char) bnext;
|
||
bFspecial = 0;
|
||
|
||
/* Rotate the checksum left. */
|
||
if ((itmpchk & 0x8000) == 0)
|
||
itmpchk <<= 1;
|
||
else
|
||
{
|
||
itmpchk <<= 1;
|
||
++itmpchk;
|
||
}
|
||
|
||
/* Add the next byte into the checksum. */
|
||
itmpchk += bnext;
|
||
}
|
||
}
|
||
|
||
if (zto != zstart)
|
||
{
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO,
|
||
"ffprocess: Calling fgot_data with %d bytes",
|
||
zto - zstart);
|
||
|
||
cFrec_data += zto - zstart;
|
||
if (! fgot_data (zstart, zto - zstart, FALSE, TRUE, pfexit))
|
||
return FALSE;
|
||
}
|
||
|
||
cFrec_bytes += zfrom - zstart;
|
||
|
||
iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN;
|
||
}
|
||
|
||
iFcheck = itmpchk;
|
||
|
||
if (pcneed != NULL)
|
||
{
|
||
/* At this point we may have seen the first 0176 in the checksum
|
||
but not the second. The checksum is at least seven
|
||
characters long (0176 0176 a b c d \r). This won't help
|
||
much, but reading seven characters is a lot better than
|
||
reading two, which is what I saw in a 2400 baud log file. */
|
||
if (bFspecial == 0176)
|
||
*pcneed = 6;
|
||
else
|
||
*pcneed = 7;
|
||
}
|
||
|
||
*pfexit = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
/* Wait for data to come in and process it until we've finished a
|
||
command or a file. */
|
||
|
||
boolean
|
||
ffwait ()
|
||
{
|
||
while (TRUE)
|
||
{
|
||
boolean fexit;
|
||
int cneed, crec;
|
||
|
||
if (! ffprocess_data (&fexit, &cneed))
|
||
return FALSE;
|
||
if (fexit)
|
||
return TRUE;
|
||
|
||
/* We only ask for one character at a time. This could wind up
|
||
being quite inefficient, since we might only get one
|
||
character back from each read. We really want to do
|
||
something like get all available characters, then sleep for
|
||
half a second and get all available characters again, and
|
||
keep this up until we don't get anything after sleeping. */
|
||
|
||
if (! freceive_data (cneed, &crec, cFtimeout, TRUE))
|
||
return FALSE;
|
||
|
||
if (crec == 0)
|
||
{
|
||
ulog (LOG_ERROR, "Timed out waiting for data");
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* File level operations. Reset the checksums when starting to send
|
||
or receive a file, and output the checksum when we've finished
|
||
sending a file. */
|
||
|
||
/*ARGSUSED*/
|
||
boolean
|
||
fffile (fstart, fsend, pfredo, cbytes)
|
||
boolean fstart;
|
||
boolean fsend;
|
||
boolean *pfredo;
|
||
long cbytes;
|
||
{
|
||
if (fstart)
|
||
{
|
||
iFcheck = 0xffff;
|
||
cFretries = 0;
|
||
if (! fsend)
|
||
{
|
||
bFspecial = 0;
|
||
fFfile = TRUE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
const char *z;
|
||
|
||
*pfredo = FALSE;
|
||
|
||
if (fsend)
|
||
{
|
||
char ab[8];
|
||
|
||
/* Send the final checksum. */
|
||
|
||
sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff);
|
||
if (! fsend_data (ab, 7, TRUE))
|
||
return FALSE;
|
||
|
||
/* Now look for the acknowledgement. */
|
||
z = zgetcmd ();
|
||
if (z == NULL)
|
||
return FALSE;
|
||
|
||
/* An R means to retry sending the file. */
|
||
if (*z == 'R')
|
||
{
|
||
++cFretries;
|
||
if (cFretries > cFmaxretries)
|
||
{
|
||
ulog (LOG_ERROR, "Too many retries");
|
||
return FALSE;
|
||
}
|
||
*pfredo = TRUE;
|
||
iFcheck = 0xffff;
|
||
++cFsend_retries;
|
||
return TRUE;
|
||
}
|
||
|
||
if (*z == 'G')
|
||
return TRUE;
|
||
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO, "fffile: Got \"%s\"", z);
|
||
|
||
ulog (LOG_ERROR, "File send failed");
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
unsigned int icheck;
|
||
|
||
/* We next expect to receive a command. */
|
||
fFfile = FALSE;
|
||
|
||
/* Get the checksum. */
|
||
z = zgetcmd ();
|
||
if (z == NULL)
|
||
return FALSE;
|
||
|
||
if (strlen (z) != 4
|
||
|| ! isxdigit (z[0])
|
||
|| ! isxdigit (z[1])
|
||
|| ! isxdigit (z[2])
|
||
|| ! isxdigit (z[3]))
|
||
{
|
||
ulog (LOG_ERROR, "Bad checksum format");
|
||
return FALSE;
|
||
}
|
||
|
||
icheck = strtol (z, (char **) NULL, 16);
|
||
|
||
if (icheck != (iFcheck & 0xffff))
|
||
{
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
|
||
"Checksum failed; calculated 0x%x, got 0x%x",
|
||
iFcheck & 0xffff, icheck);
|
||
|
||
++cFretries;
|
||
if (cFretries > cFmaxretries)
|
||
{
|
||
ulog (LOG_ERROR, "Too many retries");
|
||
(void) ffsendcmd ("Q");
|
||
return FALSE;
|
||
}
|
||
|
||
*pfredo = TRUE;
|
||
iFcheck = 0xffff;
|
||
bFspecial = 0;
|
||
fFfile = TRUE;
|
||
++cFrec_retries;
|
||
|
||
/* Send an R to tell the other side to resend the file. */
|
||
return ffsendcmd ("R");
|
||
}
|
||
|
||
/* Send a G to tell the other side the file was received
|
||
correctly. */
|
||
return ffsendcmd ("G");
|
||
}
|
||
}
|
||
}
|