summaryrefslogtreecommitdiffstats
path: root/gnu/libexec/uucp/uucico/proty.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/uucico/proty.c')
-rw-r--r--gnu/libexec/uucp/uucico/proty.c660
1 files changed, 660 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/uucico/proty.c b/gnu/libexec/uucp/uucico/proty.c
new file mode 100644
index 0000000..f15671e
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/proty.c
@@ -0,0 +1,660 @@
+/* proty.c
+ The 'y' protocol.
+
+ Copyright (C) 1994, 1995 Jorge Cwik and 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char proty_id[] = "$Id: proty.c,v 1.4 1995/06/21 19:15:40 ian Rel $";
+#endif
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "trans.h"
+#include "system.h"
+#include "prot.h"
+
+/* The 'y' protocol, and this implementation, was written and designed
+ by Jorge Cwik <jorge@satlink.net>. Some of the routines, and the
+ coding style in general, were taken verbatim or adapted from other
+ Taylor UUCP modules. Mark Delany made the initial testings and
+ helped in portability issues.
+
+ This protocol does not perform any kind of error correction or flow
+ control. It does do error checking. It does not require an end to
+ end reliable link. It is recommended for error-free (also called
+ semi-reliable) connections as provided by error correcting modems.
+ It needs an eight bit clean channel and some kind of flow control
+ at the lower layers, typically RTS/CTS hardware flow control.
+
+ The flow of the file transmission is completely unidirectional.
+ There are no ACKs or NAKs outside file boundaries. This makes it
+ very suitable for half duplex modulations (like PEP) and
+ connections with very long delays, like multihop satellite links. */
+
+/* This protocol uses 16 bit little-endian ints in the packet header. */
+#define FROMLITTLE(p) (((p)[0] & 0xff) + (((p)[1] & 0xff) << 8))
+#define TOLITTLE(p, i) ((p)[0] = (i) & 0xff, (p)[1] = ((i) >> 8) & 0xff)
+
+/* The buffer and packet size we use. */
+#define CYBUFSIZE (1024)
+#define IYPACKSIZE (1024)
+
+/* The offset in the buffer to the data. */
+#define CYFRAMELEN (6)
+
+/* Offsets in a packet header. */
+#define YFRAME_SEQ_OFF (0)
+#define YFRAME_LEN_OFF (2)
+#define YFRAME_CTL_OFF (2)
+#define YFRAME_CHK_OFF (4)
+
+/* Offsets in a packet header viewed as an array of shorts. */
+#define YFRAME_SEQ (0)
+#define YFRAME_LEN (1)
+#define YFRAME_CTL (1)
+#define YFRAME_CHK (2)
+
+/* The default timeout. */
+#define CYTIMEOUT (60)
+
+/* Control packet types. */
+#define YPKT_ACK (0xFFFE)
+#define YPKT_ERR (0xFFFD)
+#define YPKT_BAD (0xFFFC)
+
+/* The protocol version number. */
+#define Y_VERSION (1)
+
+/* When the protocol starts up, it transmit the following information:
+ 1 byte version
+ 1 byte packet size
+ 2 byte flags (none currently defined)
+ Future revision may expand the structure as long as these members
+ keep their current offset. */
+#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)
+
+/* The initialization length of the lowest accepted version. */
+#define MIN_Y_SYNC (4)
+
+/* Not strictly needed, but I would not want to accept a 32k sync pkt. */
+#define MAX_Y_SYNC IYPACKSIZE
+
+/* Local and remote packet sizes (we actually use the same value for
+ both). */
+static size_t iYlocal_packsize = IYPACKSIZE;
+static size_t iYremote_packsize = IYPACKSIZE;
+
+/* Local and remote packet sequence numbers. */
+static unsigned short iYlocal_pktnum;
+static unsigned short iYremote_pktnum;
+
+/* The timeout. */
+static int cYtimeout = CYTIMEOUT;
+
+/* Transmitter buffer. */
+static char *zYbuf;
+
+/* Protocol parameters. */
+
+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 }
+};
+
+/* Local functions. */
+
+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));
+
+/* Exchange sync packets at protocol startup. */
+
+static boolean
+fyxchg_syncs (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ char inithdr[Y_INIT_HDR_LEN];
+ unsigned short header[3];
+ unsigned short ichk;
+ size_t clen, cfirst;
+ int rpktsize;
+
+ /* Send our configuration. We could use only one array (for local
+ and remote). But this is safer in case the code changes and
+ depend on separate ones. */
+
+ 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;
+ }
+
+ /* It may be better to integrate this code with fywait_for_packet. */
+ 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]);
+
+ /* Future versions of the protocol may need to check and react
+ according to the version number. */
+
+ if (rpktsize == 0 || header[YFRAME_CHK] != ichk)
+ {
+ ulog (LOG_ERROR, "Bad 'y' protocol sync packet");
+ return FALSE;
+ }
+
+ iYremote_packsize = rpktsize << 8;
+
+ /* Some may want to do this different and in effect the protocol
+ support this. But I like the idea that the packet size would be
+ the same in both directions. This allows the caller to select
+ both packet sizes without changing the configuration at the
+ server. */
+ if (iYremote_packsize > iYlocal_packsize)
+ iYremote_packsize = iYlocal_packsize;
+
+ iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
+ return TRUE;
+}
+
+/* Start the protocol. */
+
+boolean
+fystart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ *pzlog = NULL;
+
+ /* This should force, or at least enable if available, RTS/CTS
+ hardware flow control !! */
+
+ /* The 'y' protocol requires an eight bit clean link */
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+
+ iYlocal_pktnum = iYremote_pktnum = 0;
+
+ /* Only multiple of 256 sizes are allowed */
+ iYlocal_packsize &= ~0xff;
+ if (iYlocal_packsize < 256 || iYlocal_packsize > (16*1024))
+ iYlocal_packsize = IYPACKSIZE;
+
+ /* Exhange SYNC packets */
+ if (! fyxchg_syncs (qdaemon))
+ {
+ /* Restore defaults */
+ cYtimeout = CYTIMEOUT;
+ iYlocal_packsize = IYPACKSIZE;
+ return FALSE;
+ }
+
+ zYbuf = (char *) xmalloc (CYBUFSIZE + CYFRAMELEN);
+ return TRUE;
+}
+
+/* Shutdown the protocol. */
+
+boolean
+fyshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ xfree ((pointer) zYbuf);
+ zYbuf = NULL;
+ cYtimeout = CYTIMEOUT;
+ iYlocal_packsize = IYPACKSIZE;
+ return TRUE;
+}
+
+/* Send a command string. We send packets containing the string until
+ the entire string has been sent, including the zero terminator. */
+
+/*ARGSUSED*/
+boolean
+fysendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ 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;
+}
+
+/* Get space to be filled with data. We always use zYbuf, which was
+ allocated from the heap. */
+
+char *
+zygetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = iYremote_packsize;
+ return zYbuf + CYFRAMELEN;
+}
+
+/* Send out a data packet. */
+
+boolean
+fysenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+#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));
+
+ /* We pass FALSE to fsend_data since we don't expect the other side
+ to be sending us anything just now. */
+ return fsend_data (qdaemon->qconn, zYbuf, cdata + CYFRAMELEN, FALSE);
+}
+
+/* Wait for data to come in and process it until we've finished a
+ command or a file. */
+
+boolean
+fywait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ boolean fexit = FALSE;
+
+ while (! fexit)
+ {
+ if (! fywait_for_packet (qdaemon, &fexit))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* File level routines
+ We could handle this inside the other public routines,
+ but this is cleaner and better for future expansions */
+
+boolean
+fyfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+ boolean fstart;
+ boolean fsend;
+ long cbytes;
+ boolean *pfhandled;
+{
+ unsigned short header[3];
+
+ *pfhandled = FALSE;
+
+ if (! fstart)
+ {
+ if (fsend)
+ {
+ /* It is critical that the timeout here would be long
+ enough. We have just sent a full file without any kind
+ of flow control at the protocol level. The traffic may
+ be buffered in many places of the link, and the remote
+ may take a while until cathing up. */
+ 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
+ {
+ /* This is technically not requireed. But I've put this in
+ the protocol to allow easier expansions. */
+ return fysend_control (qdaemon, YPKT_ACK);
+ }
+ }
+
+ return TRUE;
+}
+
+/* Send a control packet, not used during the normal file
+ transmission. */
+
+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);
+}
+
+/* Private function to send a packet. This one doesn't need the data
+ to be in the buffer provided by zygetspace. I've found it worth
+ for avoiding memory copies. Somebody may want to do it otherwise */
+
+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);
+}
+
+/* Wait until enough data arrived from the comm line. This protocol
+ doesn't need to perform any kind of action while waiting. */
+
+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 (cinbuf < clen)
+ {
+ if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
+ timeout, TRUE))
+ return FALSE;
+ cinbuf += crec;
+ if (cinbuf < clen)
+ {
+ if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
+ timeout, TRUE))
+ return FALSE;
+ }
+ cinbuf += crec;
+ if (cinbuf < clen)
+ {
+ ulog (LOG_ERROR, "Timed out waiting for data");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Receive a remote packet header, check for correct sequence number. */
+
+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;
+
+ /* Somebody may want to optimize this in a portable way. I'm not
+ sure it's worth, but the output by gcc for the portable construct
+ is so bad (even with optimization), that I couldn't resist. */
+
+ 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;
+}
+
+/* Receive a remote data packet */
+
+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)
+ {
+ /* I Suppose the pointers could be 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;
+ }
+
+ /* This is really not neccessary. But if this check is removed,
+ take in mind that the packet may be up to 32k long. */
+ 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;
+}
+
+/* Compute 16 bit checksum */
+
+#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 the first buffer has been finished, switch to the second. */
+ if (--cfirst == 0)
+ z = zsecond;
+ }
+
+ return ichk;
+}
OpenPOWER on IntegriCloud