diff options
Diffstat (limited to 'gnu/libexec/uucp/uucico/protf.c')
| -rw-r--r-- | gnu/libexec/uucp/uucico/protf.c | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/uucico/protf.c b/gnu/libexec/uucp/uucico/protf.c new file mode 100644 index 000000000000..96b213157cee --- /dev/null +++ b/gnu/libexec/uucp/uucico/protf.c @@ -0,0 +1,842 @@ +/* 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 Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char protf_rcsid[] = "$Id: protf.c,v 1.1 1993/08/05 18:27:10 conklin Exp $"; +#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" + +/* 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. */ + +/* This structure is used to hold information when dealing with the + end of file acknowledgement. */ + +struct sfinfo +{ + /* The functions from the generic code. */ + boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon)); + boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); + /* The info pointer from the generic code. */ + pointer pinfo; + /* The character to send after receiving the checksum. */ + char bsend; +}; + +/* Internal functions. */ +static boolean ffprocess_data P((struct sdaemon *qdaemon, + boolean *pfexit, size_t *pcneed)); +static boolean ffawait_ack P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean ffawait_cksum P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean ffsend_ack P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* 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; + +/* Whether this file has been acknowledged. */ +static boolean fFacked; + +struct uuconf_cmdtab asFproto_params[] = +{ + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cFtimeout, NULL }, + { "retries", UUCONF_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. */ + +boolean +ffstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + *pzlog = NULL; + + cFsent_data = 0; + cFsent_bytes = 0; + cFrec_data = 0; + cFrec_bytes = 0; + cFsend_retries = 0; + cFrec_retries = 0; + + /* Use XON/XOFF handshaking. */ + if (! fconn_set (qdaemon->qconn, PARITYSETTING_DEFAULT, + STRIPSETTING_SEVENBITS, XONXOFF_ON)) + 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. */ + +/*ARGSIGNORED*/ +boolean +ffshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + 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); + cFtimeout = 120; + cFmaxretries = 2; + return TRUE; +} + +/* Send a command string. We just send the string followed by a carriage + return. */ + +/*ARGSUSED*/ +boolean +ffsendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + size_t clen; + char *zalc; + boolean fret; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ffsendcmd: Sending command \"%s\"", z); + + clen = strlen (z); + zalc = zbufalc (clen + 2); + memcpy (zalc, z, clen); + zalc[clen] = '\r'; + zalc[clen + 1] = '\0'; + fret = fsend_data (qdaemon->qconn, zalc, clen + 1, TRUE); + ubuffree (zalc); + return fret; +} + +/* Get space to be filled with data. We allocate the space from the + heap. */ + +/*ARGSIGNORED*/ +char * +zfgetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *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. */ + +/*ARGSIGNORED*/ +boolean +ffsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ + 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 (qdaemon->qconn, ab, (size_t) (ze - ab), FALSE); +} + +/* 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 (qdaemon, pfexit, pcneed) + struct sdaemon *qdaemon; + boolean *pfexit; + size_t *pcneed; +{ + int i; + register unsigned int itmpchk; + + *pfexit = FALSE; + 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; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ffprocess_data: Got %d command bytes", + i - iPrecstart + 1); + + abPrecbuf[i] = '\0'; + istart = iPrecstart; + iPrecstart = (i + 1) % CRECBUFLEN; + if (pcneed != NULL) + *pcneed = 0; + return fgot_data (qdaemon, abPrecbuf + istart, + (size_t) (i - istart + 1), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit); + } + } + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ffprocess_data: Got %d command bytes", + i - iPrecstart); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) (i - iPrecstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = i % CRECBUFLEN; + } + + 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 (qdaemon, zstart, + (size_t) (zto - zstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, 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 wind up calling + fffile to verify the checksum. We set *pcneed to + 0 because we don't want to read any more data + from the port, since we may have already read the + checksum. */ + if (pcneed != NULL) + *pcneed = 0; + return fgot_data (qdaemon, (const char *) NULL, + (size_t) 0, (const char *) NULL, + (size_t) 0, -1, -1, (long) -1, + 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_data: Got %d bytes", + zto - zstart); + + cFrec_data += zto - zstart; + if (! fgot_data (qdaemon, zstart, (size_t) (zto - zstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, 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; + } + + return TRUE; +} + +/* Wait for data to come in and process it until we've finished a + command or a file. */ + +boolean +ffwait (qdaemon) + struct sdaemon *qdaemon; +{ + while (TRUE) + { + boolean fexit; + size_t cneed, crec; + + if (! ffprocess_data (qdaemon, &fexit, &cneed)) + return FALSE; + if (fexit) + return TRUE; + + if (cneed > 0) + { + /* 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 (qdaemon->qconn, 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 (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) + struct sdaemon *qdaemon; + struct stransfer *qtrans; + boolean fstart; + boolean fsend; + long cbytes; + boolean *pfhandled; +{ + DEBUG_MESSAGE3 (DEBUG_PROTO, "fffile: fstart %s; fsend %s; fFacked %s", + fstart ? "true" : "false", fsend ? "true" : "false", + fFacked ? "true" : "false"); + + *pfhandled = FALSE; + + if (fstart) + { + iFcheck = 0xffff; + cFretries = 0; + fFacked = FALSE; + if (! fsend) + { + bFspecial = 0; + fFfile = TRUE; + } + return TRUE; + } + else + { + struct sfinfo *qinfo; + + /* We need to handle the checksum and the acknowledgement. If + we get a successful ACK, we set fFacked to TRUE and call the + send or receive function by hand. This will wind up calling + here again, so if fFacked is TRUE we just return out and let + the send or receive function do whatever it does. This is a + bit of a hack. */ + if (fFacked) + { + fFacked = FALSE; + return TRUE; + } + + if (fsend) + { + char ab[sizeof "\176\176ABCD\r"]; + + /* Send the final checksum. */ + sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff); + if (! fsend_data (qdaemon->qconn, ab, (size_t) 7, TRUE)) + return FALSE; + + /* Now wait for the acknowledgement. */ + fFfile = FALSE; + qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); + qinfo->psendfn = qtrans->psendfn; + qinfo->precfn = qtrans->precfn; + qinfo->pinfo = qtrans->pinfo; + qtrans->psendfn = NULL; + qtrans->precfn = ffawait_ack; + qtrans->pinfo = (pointer) qinfo; + qtrans->fcmd = TRUE; + + *pfhandled = TRUE; + + return fqueue_receive (qdaemon, qtrans); + } + else + { + /* Wait for the checksum. */ + fFfile = FALSE; + qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); + qinfo->psendfn = qtrans->psendfn; + qinfo->precfn = qtrans->precfn; + qinfo->pinfo = qtrans->pinfo; + qtrans->psendfn = NULL; + qtrans->precfn = ffawait_cksum; + qtrans->pinfo = (pointer) qinfo; + qtrans->fcmd = TRUE; + + *pfhandled = TRUE; + + return fqueue_receive (qdaemon, qtrans); + } + } +} + +/* Wait for the ack after sending a file and the checksum. */ + +static boolean +ffawait_ack (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + + qtrans->precfn = NULL; + + /* An R means to retry sending the file. */ + if (*zdata == 'R') + { + if (! ffileisopen (qtrans->e)) + { + ulog (LOG_ERROR, "Request to resent non-file"); + return FALSE; + } + + ++cFretries; + if (cFretries > cFmaxretries) + { + ulog (LOG_ERROR, "Too many retries"); + return FALSE; + } + + ulog (LOG_NORMAL, "Resending file"); + if (! ffilerewind (qtrans->e)) + { + ulog (LOG_ERROR, "rewind: %s", strerror (errno)); + return FALSE; + } + qtrans->ipos = (long) 0; + + iFcheck = 0xffff; + ++cFsend_retries; + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + qtrans->fsendfile = TRUE; + + return fqueue_send (qdaemon, qtrans); + } + + if (*zdata != 'G') + { + DEBUG_MESSAGE1 (DEBUG_PROTO, "fffile: Got \"%s\"", zdata); + ulog (LOG_ERROR, "File send failed"); + return FALSE; + } + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + + /* Now call the send function by hand after setting fFacked to TRUE. + Since fFacked is true fffile will simply return out, and the send + function can do whatever it what was going to do. */ + fFacked = TRUE; + return (*qtrans->psendfn) (qtrans, qdaemon); +} + +/* This function is called when the checksum arrives. */ + +/*ARGSUSED*/ +static boolean +ffawait_cksum (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + unsigned int icheck; + + qtrans->precfn = NULL; + + if (! isxdigit (zdata[0]) + || ! isxdigit (zdata[1]) + || ! isxdigit (zdata[2]) + || ! isxdigit (zdata[3]) + || zdata[4] != '\0') + { + ulog (LOG_ERROR, "Bad checksum format"); + xfree (qtrans->pinfo); + return FALSE; + } + + icheck = (unsigned int) strtol ((char *) zdata, (char **) NULL, 16); + + if (icheck != (iFcheck & 0xffff)) + { + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "Checksum failed; calculated 0x%x, got 0x%x", + iFcheck & 0xffff, icheck); + + if (! ffileisopen (qtrans->e)) + { + ulog (LOG_ERROR, "Failed to get non-file"); + return FALSE; + } + + ++cFretries; + if (cFretries > cFmaxretries) + { + ulog (LOG_ERROR, "Too many retries"); + qinfo->bsend = 'Q'; + } + else + { + ulog (LOG_NORMAL, "File being resent"); + + /* This bit of code relies on the receive code setting + qtrans->s.ztemp to the full name of the temporary file + being used. */ + qtrans->e = esysdep_truncate (qtrans->e, qtrans->s.ztemp); + if (! ffileisopen (qtrans->e)) + return FALSE; + qtrans->ipos = (long) 0; + + iFcheck = 0xffff; + bFspecial = 0; + fFfile = TRUE; + ++cFrec_retries; + + /* Send an R to tell the other side to resend the file. */ + qinfo->bsend = 'R'; + } + } + else + { + /* Send a G to tell the other side the file was received + correctly. */ + qinfo->bsend = 'G'; + } + + qtrans->psendfn = ffsend_ack; + + return fqueue_send (qdaemon, qtrans); +} + +/* Send the acknowledgement, and then possible wait for the resent + file. */ + +static boolean +ffsend_ack (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + char ab[2]; + + ab[0] = qinfo->bsend; + ab[1] = '\0'; + if (! ffsendcmd (qdaemon, ab, 0, 0)) + return FALSE; + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + + if (ab[0] == 'Q') + return FALSE; + if (ab[0] == 'R') + { + qtrans->frecfile = TRUE; + return fqueue_receive (qdaemon, qtrans); + } + + fFacked = TRUE; + return (*qtrans->precfn) (qtrans, qdaemon, (const char *) NULL, + (size_t) 0); +} |
