diff options
Diffstat (limited to 'gnu/libexec/uucp/uucico/proty.c')
-rw-r--r-- | gnu/libexec/uucp/uucico/proty.c | 660 |
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; +} |