mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
2627 lines
67 KiB
C
2627 lines
67 KiB
C
/* protz.c Version 1.5, 92Apr24 */
|
||
/* Modified by Ian Lance Taylor for Taylor UUCP 1.04 92Aug4. */
|
||
|
||
/*
|
||
* Doug Evans, dje@sspiff.UUCP or dje@ersys.edmonton.ab.ca
|
||
*
|
||
* This file provides the Zmodem protocol (by Chuck Forsberg) for
|
||
* Ian Taylor's UUCP package.
|
||
*
|
||
* It was originally developed to establish a uucp link between myself and my
|
||
* employer: Ivation Datasystems, Inc. of Ottawa.
|
||
*
|
||
* My thanks to Ivation for letting me release this to the public. Given that
|
||
* Zmodem is in the public domain, no additional copyrights have been added.
|
||
*
|
||
*****************************************************************************
|
||
*
|
||
* It's been difficult fitting Zmodem into the UUCP world. I have been guided
|
||
* mostly by trying to plug it into Taylor UUCP. Where "the Zmodem way of doing
|
||
* things" conflicted with "the UUCP way of doing things", I have err'd on the
|
||
* side of UUCP. At the end of it all, I have achieved something that will plug
|
||
* into Taylor UUCP very easily, but some might argue that I have corrupted Z
|
||
* too much. At any rate, compatibility with sz/rz was sacrificed to achieve a
|
||
* clean UUCP protocol. Given that, I took the opportunity to start from
|
||
* scratch when defining protocol constants (EG: ZBIN).
|
||
*
|
||
* 1) I wasn't quite sure how to enhance Zmodem to handle send+receive in one
|
||
* session, so I added a 'g' protocol like initialization sequence. This
|
||
* also gets this stuff out of the way, in case we ever try to support
|
||
* full-duplex.
|
||
*
|
||
* Caller Callee
|
||
* ------ ------
|
||
* ZINIT --> <-- ZINIT
|
||
* ZDATA (ZCRCF) --> <-- ZDATA (ZCRCF)
|
||
* ZACK --> <-- ZACK
|
||
* ZINITEND --> <-- ZINITEND
|
||
*
|
||
* ZINIT is a combination of ZRINIT and ZSINIT and is intended to exchange
|
||
* simple protocol information (flags) and the protocol version number.
|
||
* ZDATA is intended to include window size information as well as the
|
||
* "Myattn" string (although at the moment it doesn't contain anything).
|
||
* ZDATA may contain at most 1k bytes of data and is sent out as one ZCRCF
|
||
* packet. Two ack's (ZACK + ZINITEND) are needed to ensure both sides have
|
||
* received ZDATA.
|
||
*
|
||
* 2) I've hardcoded several protocol parameters, like 32 bit CRC's for data.
|
||
* Others are not supported (we don't need them).
|
||
*
|
||
* 3) ZHEX headers use 32 bit CRC's.
|
||
*
|
||
* 4) Zmodem sends the ZFILE message "in one chunk". If there are errors, the
|
||
* entire string is resent. I have continued this practice. All UUCP
|
||
* commands are sent "in one chunk". This can be changed down the road if
|
||
* necessary.
|
||
*
|
||
* 5) The ZEOF message has been replaced with a new ZCRCx value: ZCRCF. ZCRCF
|
||
* is identical to ZCRCW except that it indicates the end of the message.
|
||
* The protocol here is *not* a file transfer protocol. It is an end to end
|
||
* transport protocol (that preserves message boundaries).
|
||
*
|
||
* 6) Zmodem handles restarting a file transfer, but as best as I can tell UUCP
|
||
* does not. At least Taylor UUCP doesn't. And if UUCP does start handling
|
||
* file restart, can it be plugged into the existing Zmodem way with zero
|
||
* changes? Beats me. Therefore I have removed this part of the code. One
|
||
* can always put it back in if and when UUCP handles it. Ditto for other
|
||
* pieces of removed code: there's no point in overly complicating this code
|
||
* when supporting all the bells and whistles requires enhancements to UUCP
|
||
* itself.
|
||
*
|
||
* *** It is easier to put code back in in an upward compatible manner ***
|
||
* *** than it is to correct for misunderstood code or poorly merged ***
|
||
* *** (Zmodem vs UUCP) code. ***
|
||
*
|
||
* 7) For the character in the initial "protocol selection" sequence, I have
|
||
* chosen 'a'. I'm told 'z' is already in use for something that isn't
|
||
* Zmodem. It's entirely reasonable to believe that if Zmodem ever becomes a
|
||
* standard UUCP protocol, this won't be it (so I'll leave z/Z for them).
|
||
* Publicly, this is the 'a' protocol. Internally, it is refered to as 'z'.
|
||
* A little confusing, I know. Maybe in time I'll refer to it internally as
|
||
* 'a', or maybe in time this will be *the* 'z' protocol.
|
||
*
|
||
* 8) Since we are writing a transport protocol, which isn't supposed to know
|
||
* anything about what is being transfered or where it is coming from, the
|
||
* header data value has changed meaning. It no longer means "file position"
|
||
* but instead means "window position". It is a running counter of the bytes
|
||
* transfered. Each "message" begins on a 1k boundary so the count isn't a
|
||
* precise byte count. The counter wraps every 4 gigabytes, although this
|
||
* wrapping isn't supported yet.
|
||
*
|
||
* FIXME: At present the max data transfered per session is 4 gigabytes.
|
||
*
|
||
****************************************************************************
|
||
*
|
||
* A typical message sequence is (master sending file to slave):
|
||
*
|
||
* Master Slave
|
||
* ------ -----
|
||
* ZDATA (S, ZCRCF) -->
|
||
* <-- ZACK
|
||
* <-- ZDATA (SY, ZCRCF)
|
||
* ZACK -->
|
||
* ZDATA -->
|
||
* ... <-- ZACK/ZRPOS
|
||
* ZDATA (ZCRCF) -->
|
||
* <-- ZACK
|
||
* <-- ZDATA (CY, ZCRCF)
|
||
* ZACK -->
|
||
*
|
||
* A typical message sequence is (master receiving file from slave):
|
||
*
|
||
* Master Slave
|
||
* ------ -----
|
||
* ZDATA (R, ZCRCF) -->
|
||
* <-- ZACK
|
||
* <-- ZDATA (RY, ZCRCF)
|
||
* ZACK -->
|
||
* <-- ZDATA
|
||
* ZACK/ZRPOS ... -->
|
||
* <-- ZDATA (ZCRCF)
|
||
* ZACK -->
|
||
* ZDATA (CY, ZCRCF) -->
|
||
* <-- ZACK
|
||
*
|
||
*****************************************************************************
|
||
*
|
||
* Notes:
|
||
* 1) For future bidirectional concerns, keep packet types "unidirectional".
|
||
* Sender always uses: ZDATA, ZNAK
|
||
* Receiver always uses: ZRPOS, ZACK
|
||
* There is no intersection.
|
||
*
|
||
* I'm not sure if this is necessary or even useful, but it seems to be.
|
||
*
|
||
* 2) I use to store the byte count / 32 in the data header. This left 5 bits
|
||
* unused for future concerns. I removed this because of the following
|
||
* situation when sending a file:
|
||
*
|
||
* ZDATA (ZCRCG, xx bytes) - received ok
|
||
* ZDATA (ZCRCF, 0 bytes) - corrupted
|
||
*
|
||
* At this point the receiver would like to send back a ZRPOS with a value
|
||
* of the size of the file. However, it can't because the value is divided
|
||
* by 32, and it would have to round up to the next multiple of 32. This
|
||
* seemed a little ugly, so I went with using the entire header to store
|
||
* the byte count.
|
||
*
|
||
*****************************************************************************
|
||
*
|
||
* Source version:
|
||
*
|
||
* 1.1,2,3
|
||
* Protocol version 0
|
||
* Early attempts, completely rewritten later.
|
||
*
|
||
* 1.4 Protocol version 1
|
||
* Beta test sent to Ian for analysis 92Apr18.
|
||
*
|
||
* 1.5 Protocol version 1
|
||
* Released 92Apr24.
|
||
*
|
||
*****************************************************************************
|
||
*
|
||
* Protocol version:
|
||
*
|
||
* A version number is exchanged in the ZINIT message, so it is possible to
|
||
* correct or enhance the protocol, without breaking existing versions.
|
||
* The purpose of this section is to document these versions as they come out.
|
||
* Remember, this is the protocol version, not the source version.
|
||
*
|
||
* 0 Initial version.
|
||
* Zmodem controlled file transfer. This was more of a "plug Z
|
||
* into UUCP as is" port.
|
||
*
|
||
* 1 Complete rewrite.
|
||
* Made Z more of a transport protocol. UUCP now controls transfer and Z
|
||
* is on the same footing as the other UUCP protocols.
|
||
* Theoretically, there will be little pain when UUCP goes bidirectional.
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
const char protz_rcsid[] = "$Id: protz.c,v 1.8 1993/12/20 00:44:49 ian Rel $";
|
||
#endif
|
||
|
||
#include <errno.h>
|
||
|
||
#include "uudefs.h"
|
||
#include "uuconf.h"
|
||
#include "conn.h"
|
||
#include "trans.h"
|
||
#include "system.h"
|
||
#include "prot.h"
|
||
|
||
#define ZPROTOCOL_VERSION 1
|
||
|
||
/*
|
||
* Control message characters ...
|
||
*/
|
||
|
||
#define ZPAD '*' /* Padding character begins frames */
|
||
#define ZDLE 030 /* Ctrl-X Zmodem escape - `ala BISYNC DLE */
|
||
#define ZBIN 'A' /* Binary frame indicator */
|
||
#define ZHEX 'B' /* HEX frame indicator */
|
||
|
||
/*
|
||
* Frame types (see array "frametypes" in zm.c) ...
|
||
*
|
||
* Note that the numbers here have been reorganized, as we don't support
|
||
* all of them (nor do we need to).
|
||
*
|
||
* WARNING: The init sequence assumes ZINIT < ZDATA < ZACK < ZINITEND.
|
||
*/
|
||
|
||
#define ZINIT 0 /* Init (contains protocol version, flags) */
|
||
#define ZDATA 1 /* Data packet(s) follow */
|
||
#define ZRPOS 2 /* Resume data trans at this position */
|
||
#define ZACK 3 /* ACK to above */
|
||
#define ZNAK 4 /* Last packet was garbled */
|
||
#define Zreserved 5 /* reserved (for future concerns) */
|
||
#define ZINITEND 6 /* end of init sequence */
|
||
#define ZFIN 7 /* Finish session */
|
||
|
||
/*
|
||
* ZDLE sequences ...
|
||
*
|
||
* Note addition of ZCRCF: "end of message".
|
||
*/
|
||
|
||
#define ZCRCE 'h' /* CRC next, frame ends, header packet follows */
|
||
#define ZCRCG 'i' /* CRC next, frame continues nonstop */
|
||
#define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */
|
||
#define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */
|
||
#define ZCRCF 'l' /* CRC next, ZACK expected, end of message */
|
||
|
||
#define ZRUB0 'm' /* Translate to rubout 0177 */
|
||
#define ZRUB1 'n' /* Translate to rubout 0377 */
|
||
|
||
|
||
/*
|
||
* zdlread return values (internal) ...
|
||
* Other values are ZM_ERROR, ZM_TIMEOUT, ZM_RCDO.
|
||
*/
|
||
|
||
#define GOTOR 0400
|
||
#define GOTCRCE (ZCRCE | GOTOR) /* ZDLE-ZCRCE received */
|
||
#define GOTCRCG (ZCRCG | GOTOR) /* ZDLE-ZCRCG received */
|
||
#define GOTCRCQ (ZCRCQ | GOTOR) /* ZDLE-ZCRCQ received */
|
||
#define GOTCRCW (ZCRCW | GOTOR) /* ZDLE-ZCRCW received */
|
||
#define GOTCRCF (ZCRCF | GOTOR) /* ZDLE-ZCRCF received */
|
||
|
||
/*
|
||
* Byte positions within header array ...
|
||
*/
|
||
|
||
#define ZF0 3 /* First flags byte */
|
||
#define ZF1 2
|
||
#define ZF2 1
|
||
#define ZF3 0
|
||
|
||
#define ZP0 0 /* Low order 8 bits of position */
|
||
#define ZP1 1
|
||
#define ZP2 2
|
||
#define ZP3 3 /* High order 8 bits of position */
|
||
|
||
/*
|
||
* Bit Masks for ZRQINIT flags byte ZF0 ...
|
||
*/
|
||
|
||
#define TX_ESCCTL 1 /* Tx will escape control chars */
|
||
|
||
/*
|
||
* Possible errors when running ZMODEM ...
|
||
*/
|
||
|
||
#define ZM_ERROR (-1) /* crc error, etc. */
|
||
#define ZM_TIMEOUT (-2)
|
||
#define ZM_RCDO (-3) /* Carrier Lost */
|
||
|
||
/*
|
||
* ASCII characters ...
|
||
*/
|
||
|
||
#define LF 012
|
||
#define CR 015
|
||
#define XON 021
|
||
#define XOFF 023
|
||
|
||
#define XON_WAIT 10 /* seconds */
|
||
|
||
/*
|
||
* Packet sizes ...
|
||
*
|
||
* FIXME: CPACKETSIZE is hardcoded in a lot of places.
|
||
* It's not clear to me whether changing it's value would be a
|
||
* "good thing" or not. But of course that doesn't excuse the hardcoding.
|
||
*/
|
||
|
||
#define CPACKETSIZE 1024 /* max packet size (data only) */
|
||
#define CFRAMELEN 12 /* header size */
|
||
#define CSUFFIXLEN 10 /* suffix at end of data packets */
|
||
#define CEXCHANGE_INIT_RETRIES 4
|
||
|
||
/* The header CRC value. */
|
||
|
||
#if ANSI_C
|
||
#define IHDRCRC 0xDEBB20E3UL
|
||
#else
|
||
#define IHDRCRC ((unsigned long) 0xDEBB20E3L)
|
||
#endif
|
||
|
||
/* packet buffer size */
|
||
#define CPACKBUFSIZE (CFRAMELEN + 2 * CPACKETSIZE + CSUFFIXLEN + 42 /*slop*/)
|
||
|
||
/*
|
||
* Data types ...
|
||
*/
|
||
|
||
typedef unsigned char achdrval_t[4];
|
||
typedef unsigned long hdrval_t;
|
||
typedef unsigned long winpos_t;
|
||
|
||
/*
|
||
* Configurable parms ...
|
||
*
|
||
* FIXME: <cZrx_buf_len> isn't used yet. It may not be needed.
|
||
*/
|
||
|
||
#define CTIMEOUT 10
|
||
#define CRETRIES 10
|
||
#define CSTARTUP_RETRIES 4
|
||
#define CGARBAGE 2400
|
||
#define CSEND_WINDOW 16384
|
||
#define FESCAPE_CONTROL FALSE
|
||
|
||
static int cZtimeout = CTIMEOUT; /* (seconds) */
|
||
static int cZretries = CRETRIES;
|
||
static int cZstartup_retries = CSTARTUP_RETRIES;
|
||
static int cZmax_garbage = CGARBAGE; /* max garbage before header */
|
||
static int cZtx_window = CSEND_WINDOW; /* our transmission window */
|
||
static int cZrx_buf_len = 0; /* our reception buffer size */
|
||
static boolean fZesc_ctl = FESCAPE_CONTROL; /* escape control chars */
|
||
|
||
struct uuconf_cmdtab asZproto_params[] =
|
||
{
|
||
{"timeout", UUCONF_CMDTABTYPE_INT, (pointer) & cZtimeout, NULL},
|
||
{"retries", UUCONF_CMDTABTYPE_INT, (pointer) & cZretries, NULL},
|
||
{"startup-retries", UUCONF_CMDTABTYPE_INT,
|
||
(pointer) & cZstartup_retries, NULL},
|
||
{"garbage", UUCONF_CMDTABTYPE_INT, (pointer) & cZmax_garbage, NULL},
|
||
{"send-window", UUCONF_CMDTABTYPE_INT, (pointer) & cZtx_window, NULL},
|
||
{"escape-control", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) & fZesc_ctl,
|
||
NULL},
|
||
{NULL, 0, NULL, NULL}
|
||
};
|
||
|
||
/*
|
||
* Variables for statistic gathering ...
|
||
*
|
||
* We use <wpZtxpos, wpZrxbytes> to record the number of "packets"
|
||
* sent/received. Packets is in double quotes because some of them aren't full.
|
||
*/
|
||
|
||
static unsigned long cZheaders_sent;
|
||
static unsigned long cZheaders_received;
|
||
static unsigned long cZbytes_resent;
|
||
static unsigned long cZtimeouts;
|
||
static unsigned long cZerrors;
|
||
|
||
/*
|
||
* Data buffers ...
|
||
*/
|
||
|
||
static char *zZtx_buf; /* transmit buffer */
|
||
|
||
static char *zZtx_packet_buf; /* raw outgoing packet data */
|
||
static char *zZrx_packet_buf; /* raw incoming packet data */
|
||
|
||
/*
|
||
* Transmitter state variables ...
|
||
*/
|
||
|
||
static unsigned cZblklen; /* data length in sent/received packets */
|
||
static unsigned cZtxwspac; /* spacing between ZCRCQ requests */
|
||
/*static unsigned cZblklen_override;*//* override value for <cZblklen> */
|
||
static unsigned cZtxwcnt; /* counter used to space ack requests */
|
||
static unsigned cZrxwcnt; /* counter used to watch receiver's buf size */
|
||
static winpos_t wpZtxstart; /* <wpZtxpos> when message started */
|
||
static winpos_t wpZtxpos; /* transmitter position */
|
||
static winpos_t wpZlastsync; /* last offset to which we got a ZRPOS */
|
||
static winpos_t wpZlrxpos; /* receiver's last reported offset */
|
||
static winpos_t wpZrxpos; /* receiver file position */
|
||
|
||
static int iZlast_tx_data_packet; /* type of last ZDATA packet (ZCRCx) */
|
||
static int iZjunk_count; /* amount of garbage characters received */
|
||
static int iZtleft; /* for dynamic packet resizing */
|
||
|
||
static int iZbeenhereb4; /* times we've been ZRPOS'd to same place */
|
||
|
||
/*
|
||
* Receiver state variables ...
|
||
*/
|
||
|
||
static winpos_t wpZrxbytes; /* receiver byte count */
|
||
static int iZlast_rx_data_packet; /* last successfully received ZCRCx packet */
|
||
|
||
/*
|
||
* Misc. globals ...
|
||
*/
|
||
|
||
static char xon = XON;
|
||
|
||
#ifdef DJE_TESTING
|
||
int uucptest = -1;
|
||
int uucptest2;
|
||
int uucptestseed;
|
||
#endif
|
||
|
||
/*
|
||
* Kludge!!!
|
||
* See fzfinish_tx(). Basically the next two globals are used to record the
|
||
* fact that we got a ZDATA, but aren't quite ready to process it.
|
||
*/
|
||
|
||
static int iZpkt_rcvd_kludge; /* -1 if not valid */
|
||
static hdrval_t hvZpkt_hdrval_kludge;
|
||
|
||
/*
|
||
* Packet types ...
|
||
*/
|
||
|
||
static const char *azZframe_types[] = {
|
||
"Carrier Lost", /* -3 */
|
||
"Timeout", /* -2 */
|
||
"Error", /* -1 */
|
||
#define FTOFFSET 3
|
||
"ZINIT",
|
||
"ZDATA",
|
||
"ZRPOS",
|
||
"ZACK",
|
||
"ZNAK",
|
||
"Zreserved",
|
||
"ZINITEND",
|
||
"ZFIN",
|
||
"UNKNOWN!!!"
|
||
};
|
||
#define FTNUMBER (sizeof(azZframe_types) / sizeof(char *))
|
||
|
||
#ifndef min
|
||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||
#endif
|
||
#define ZZHEADER_NAME(itype) \
|
||
azZframe_types[min((itype) + FTOFFSET, FTNUMBER - 1)]
|
||
|
||
/*
|
||
* Local functions ...
|
||
*/
|
||
|
||
static boolean fzsend_data P((struct sdaemon *qdaemon, char *zdata,
|
||
size_t cdata, boolean fendofmessage));
|
||
static boolean fzprocess P((struct sdaemon *qdaemon));
|
||
static boolean fzstart_proto P((struct sdaemon *qdaemon));
|
||
static int izexchange_init P((struct sdaemon *qdaemon, int send_type,
|
||
achdrval_t send_val, achdrval_t recv_val));
|
||
static boolean fzshutdown_proto P((struct sdaemon *qdaemon));
|
||
static boolean fzstart_tx P((void));
|
||
static boolean fzfinish_tx P((struct sdaemon *qdaemon, long *plredo));
|
||
static boolean fzstart_rx P((void));
|
||
static boolean fzfinish_rx P((struct sdaemon *qdaemon));
|
||
static boolean fzsend_hdr P((struct sdaemon *qdaemon, int ipkttype,
|
||
int ihdrtype, hdrval_t hdrval,
|
||
boolean fcheckreceive));
|
||
static boolean fzsend_data_packet P((struct sdaemon *qdaemon, char *zdata,
|
||
size_t cdata, int frameend,
|
||
boolean fcheckreceive));
|
||
static int czbuild_header P((char *zresult, int ipkttype, int ihdrtype,
|
||
hdrval_t hdrval));
|
||
static int czbuild_data_packet P((char *zresult, const char *zdata,
|
||
size_t cdata, int frameend));
|
||
/*
|
||
* The rest of the functions do not follow Ian's naming style. I have left
|
||
* the names the same as the original zm source. Over time, they may change.
|
||
*/
|
||
static int izrecv_hdr P((struct sdaemon *qdaemon, achdrval_t hdr));
|
||
static int zrbhdr32 P((struct sdaemon *qdaemon, achdrval_t hdr));
|
||
static int zrhhdr P((struct sdaemon *qdaemon, achdrval_t hdr));
|
||
static int zrdat32 P((struct sdaemon *qdaemon, char *buf, int length,
|
||
int *iprxcount));
|
||
static int getinsync P((struct sdaemon *qdaemon, boolean flag));
|
||
static char *zputhex P((char *p, int ch));
|
||
static char *zputchar P((char *p, int ch));
|
||
static int zgethex P((struct sdaemon *qdaemon));
|
||
static int zdlread P((struct sdaemon *qdaemon));
|
||
static int noxrd7 P((struct sdaemon *qdaemon));
|
||
static int realreadchar P((struct sdaemon *qdaemon, int timeout));
|
||
static boolean fzreceive_ready P((void));
|
||
static void stohdr P((hdrval_t pos, achdrval_t hdr));
|
||
static hdrval_t rclhdr P((achdrval_t hdr));
|
||
static hdrval_t hvzencode_data_hdr P((winpos_t cbytes));
|
||
static void zdecode_data_hdr P((hdrval_t hdrval, winpos_t *pcbytes));
|
||
static winpos_t lzupdate_rxpos P((achdrval_t rx_hdr, winpos_t rxpos,
|
||
winpos_t lrxpos, winpos_t txpos));
|
||
|
||
/*
|
||
* This macro replaces readchar() because it achieves a noticable speed up. The
|
||
* readchar() function has been renamed realreadchar(). Thanks to Ian for
|
||
* running this stuff through a profiler to find this out. Ian suggests further
|
||
* speed ups may be obtained by doing a similar thing in zrdat32().
|
||
*/
|
||
|
||
/* Assign the next character to b. */
|
||
#define READCHAR(qdaemon, b, i) \
|
||
(iPrecstart != iPrecend \
|
||
? ((b) = BUCHAR (abPrecbuf[iPrecstart]), \
|
||
iPrecstart = (iPrecstart + 1) % CRECBUFLEN) \
|
||
: ((b) = realreadchar ((qdaemon), (i))))
|
||
|
||
/************************************************************************/
|
||
|
||
|
||
/*
|
||
* Start the protocol ...
|
||
*/
|
||
|
||
boolean
|
||
fzstart(qdaemon, pzlog)
|
||
struct sdaemon *qdaemon;
|
||
char **pzlog;
|
||
{
|
||
*pzlog = zbufalc (sizeof "protocol 'a' starting: , , , , , " + 100);
|
||
sprintf (*pzlog, "protocol 'a' starting: %d, %d, %d, %d, %d, %d",
|
||
cZtimeout, cZretries, cZstartup_retries,
|
||
cZmax_garbage, cZtx_window, fZesc_ctl);
|
||
|
||
if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
|
||
STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
|
||
return FALSE;
|
||
|
||
/*
|
||
* For now, we place tight restrictions on the size of the transmit
|
||
* window. This might be relaxed in the future. If it is relaxed,
|
||
* some of these tests will stay, some will go. That is why it is
|
||
* coded like it is.
|
||
*/
|
||
|
||
if (cZtx_window % 1024 != 0 ||
|
||
cZtx_window < 4096 || cZtx_window > 65536 ||
|
||
65536 % cZtx_window != 0
|
||
) {
|
||
ulog (LOG_ERROR,
|
||
"fzstart: cZtx_window not one of 4096, 8192, 16384, 32768, 65536");
|
||
return FALSE;
|
||
}
|
||
|
||
zZtx_buf = (char *) xmalloc (CPACKETSIZE);
|
||
zZtx_packet_buf = (char *) xmalloc (CPACKBUFSIZE);
|
||
zZrx_packet_buf = (char *) xmalloc (CPACKBUFSIZE);
|
||
|
||
iZlast_tx_data_packet = -1;
|
||
iZlast_rx_data_packet = -1;
|
||
|
||
wpZtxpos = wpZlrxpos = wpZrxpos = wpZrxbytes = 0;
|
||
cZtxwspac = cZtx_window / 4;
|
||
|
||
cZheaders_sent = cZheaders_received = cZbytes_resent = 0;
|
||
cZtimeouts = cZerrors = 0;
|
||
|
||
iZpkt_rcvd_kludge = -1;
|
||
|
||
#if 0
|
||
/*
|
||
* We ensure <cZtx_window> is at least 4k, so the following is
|
||
* unnecessary. It can be put back in later if needed.
|
||
*/
|
||
if (cZblklen_override > cZtxwspac
|
||
|| (!cZblklen_override && cZtxwspac < 1024))
|
||
cZblklen_override = cZtxwspac;
|
||
#endif
|
||
|
||
#ifdef DJE_TESTING
|
||
{
|
||
extern int uucptest,uucptest2,uucptestseed;
|
||
FILE *f;
|
||
|
||
if (uucptest == -1) {
|
||
f = fopen ("/usr/local/src/bin/uucp/uucptest", "r");
|
||
if (f != NULL) {
|
||
fscanf (f, "%d %d %d",
|
||
&uucptestseed, &uucptest, &uucptest2);
|
||
fclose (f);
|
||
}
|
||
srand (uucptestseed);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Fire up the protocol (exchange init messages) ...
|
||
*/
|
||
|
||
if (!fzstart_proto (qdaemon))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Stop the protocol ...
|
||
*/
|
||
|
||
boolean
|
||
fzshutdown(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
(void) fzshutdown_proto (qdaemon);
|
||
|
||
xfree ((pointer) zZtx_buf);
|
||
xfree ((pointer) zZtx_packet_buf);
|
||
xfree ((pointer) zZrx_packet_buf);
|
||
zZtx_buf = NULL;
|
||
zZtx_packet_buf = NULL;
|
||
zZrx_packet_buf = NULL;
|
||
|
||
/*
|
||
* Print some informative statistics ...
|
||
*
|
||
* I use the word "messages" here instead of "headers" because the
|
||
* latter is jargonese.
|
||
*/
|
||
|
||
ulog (LOG_NORMAL,
|
||
"Protocol 'a' messages: sent %lu, received %lu",
|
||
cZheaders_sent, cZheaders_received);
|
||
ulog (LOG_NORMAL,
|
||
"Protocol 'a' packets: sent %lu, received %lu",
|
||
wpZtxpos / 1024, wpZrxbytes / 1024);
|
||
if (cZbytes_resent != 0 || cZtimeouts != 0 || cZerrors != 0)
|
||
ulog (LOG_NORMAL,
|
||
"Protocol 'a' errors: bytes resent %lu, timeouts %lu, errors %lu",
|
||
cZbytes_resent, cZtimeouts, cZerrors);
|
||
|
||
/*
|
||
* Reset all the parameters to their default values, so that the
|
||
* protocol parameters used for this connection do not affect the
|
||
* next one.
|
||
*/
|
||
|
||
cZtimeout = CTIMEOUT;
|
||
cZretries = CRETRIES;
|
||
cZstartup_retries = CSTARTUP_RETRIES;
|
||
cZmax_garbage = CGARBAGE;
|
||
cZtx_window = CSEND_WINDOW;
|
||
fZesc_ctl = FESCAPE_CONTROL;
|
||
|
||
cZheaders_sent = cZheaders_received = cZbytes_resent = 0;
|
||
cZtimeouts = cZerrors = 0;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Send a command string ...
|
||
* We send everything up to and including the null byte.
|
||
*
|
||
* We assume the command will fit in the outgoing data buffer.
|
||
* FIXME: A valid assumption?
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
boolean
|
||
fzsendcmd(qdaemon, z, ilocal, iremote)
|
||
struct sdaemon *qdaemon;
|
||
const char *z;
|
||
int ilocal;
|
||
int iremote;
|
||
{
|
||
size_t n,clen;
|
||
long lredo;
|
||
char *zbuf;
|
||
|
||
clen = strlen (z) + 1;
|
||
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsendcmd: sending command %s", z);
|
||
|
||
if (!fzstart_tx ()) /* must be called before zzgetspace() */
|
||
return FALSE;
|
||
|
||
if ((zbuf = zzgetspace (qdaemon, &n)) == NULL)
|
||
return FALSE;
|
||
|
||
#if DEBUG > 0
|
||
if (clen > n)
|
||
ulog (LOG_FATAL, "fzsendcmd: clen > n");
|
||
#endif
|
||
|
||
strcpy (zbuf, z);
|
||
|
||
/*
|
||
* Send it out ...
|
||
*/
|
||
|
||
do {
|
||
if (!fzsend_data (qdaemon, zbuf, clen, TRUE))
|
||
return FALSE;
|
||
if (!fzfinish_tx (qdaemon, &lredo))
|
||
return FALSE;
|
||
} while (lredo >= 0);
|
||
|
||
return fzprocess (qdaemon);
|
||
}
|
||
|
||
/*
|
||
* Allocate a packet to send out ...
|
||
*
|
||
* Note that 'z' has dynamic packet resizing and that <cZblklen> will range
|
||
* from 32 to 1024, in multiples of 2.
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
char *
|
||
zzgetspace(qdaemon, pclen)
|
||
struct sdaemon *qdaemon;
|
||
size_t *pclen;
|
||
{
|
||
*pclen = cZblklen;
|
||
return zZtx_buf;
|
||
}
|
||
|
||
/*
|
||
* Send a block of data ...
|
||
*
|
||
* If (cdata == 0) then the end of the file has been reached.
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
boolean
|
||
fzsenddata(qdaemon, zdata, cdata, ilocal, iremote, ipos)
|
||
struct sdaemon *qdaemon;
|
||
char *zdata;
|
||
size_t cdata;
|
||
int ilocal;
|
||
int iremote;
|
||
long ipos;
|
||
{
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsenddata: %d bytes", cdata);
|
||
|
||
if (! fzsend_data (qdaemon, zdata, cdata, cdata == 0))
|
||
return FALSE;
|
||
return fzprocess (qdaemon);
|
||
}
|
||
|
||
/*
|
||
* Send a block of data (command or file) ...
|
||
*/
|
||
|
||
/* This should buffer the data internally. Until it does, it needs to
|
||
be able to reset the file position when it is called. This is
|
||
really ugly. */
|
||
extern struct stransfer *qTsend;
|
||
|
||
static boolean
|
||
fzsend_data(qdaemon, zdata, cdata, fendofmessage)
|
||
struct sdaemon *qdaemon;
|
||
char *zdata;
|
||
size_t cdata;
|
||
boolean fendofmessage;
|
||
{
|
||
size_t n;
|
||
|
||
if (iZlast_tx_data_packet == -1 || iZlast_tx_data_packet == ZCRCW) {
|
||
cZtxwcnt = cZrxwcnt = 0;
|
||
iZjunk_count = 0;
|
||
if (!fzsend_hdr (qdaemon, ZBIN, ZDATA,
|
||
hvzencode_data_hdr (wpZtxpos), TRUE))
|
||
return FALSE;
|
||
}
|
||
|
||
n = cdata;
|
||
|
||
if (fendofmessage)
|
||
iZlast_tx_data_packet = ZCRCF;
|
||
else if (iZjunk_count > 3)
|
||
iZlast_tx_data_packet = ZCRCW;
|
||
else if (wpZtxpos == wpZlastsync)
|
||
iZlast_tx_data_packet = ZCRCW;
|
||
else if (cZrx_buf_len && (cZrxwcnt += n) >= cZrx_buf_len)
|
||
iZlast_tx_data_packet = ZCRCW;
|
||
else if ((cZtxwcnt += n) >= cZtxwspac) {
|
||
iZlast_tx_data_packet = ZCRCQ;
|
||
cZtxwcnt = 0;
|
||
} else
|
||
iZlast_tx_data_packet = ZCRCG;
|
||
|
||
if (++iZtleft > 3) {
|
||
iZtleft = 0;
|
||
if (cZblklen < 1024)
|
||
cZblklen *= 2;
|
||
#if 0 /* <cZblklen_override> is currently unnecessary */
|
||
if (cZblklen_override && cZblklen > cZblklen_override)
|
||
cZblklen = cZblklen_override;
|
||
#endif
|
||
if (cZblklen > 1024)
|
||
cZblklen = 1024;
|
||
if (cZrx_buf_len && cZblklen > cZrx_buf_len)
|
||
cZblklen = cZrx_buf_len;
|
||
}
|
||
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING(DEBUG_PROTO)) {
|
||
const char *type;
|
||
|
||
switch (iZlast_tx_data_packet) {
|
||
case ZCRCW: type = "ZCRCW"; break;
|
||
case ZCRCG: type = "ZCRCG"; break;
|
||
case ZCRCQ: type = "ZCRCQ"; break;
|
||
case ZCRCE: type = "ZCRCE"; break;
|
||
case ZCRCF: type = "ZCRCF"; break;
|
||
default : type = "UNKNOWN!!!"; break;
|
||
}
|
||
DEBUG_MESSAGE3 (DEBUG_PROTO,
|
||
"fzsend_data: %s, pos 0x%lx, %d bytes",
|
||
type, wpZtxpos, n);
|
||
}
|
||
#endif
|
||
|
||
if (!fzsend_data_packet (qdaemon, zdata, n, iZlast_tx_data_packet,
|
||
TRUE))
|
||
return FALSE;
|
||
|
||
wpZtxpos += n;
|
||
|
||
if (iZlast_tx_data_packet == ZCRCW) {
|
||
/*
|
||
* FIXME: Ideally this would be done in fzprocess. However, it
|
||
* is only called if there is data pending which there
|
||
* may not be yet. I could have patched fploop() a bit but
|
||
* for now, I've done it like this.
|
||
*/
|
||
switch (getinsync (qdaemon, FALSE)) {
|
||
case ZACK:
|
||
break;
|
||
case ZRPOS:
|
||
if (qTsend == NULL
|
||
|| ! ffileisopen (qTsend->e)) {
|
||
ulog (LOG_ERROR, "Can't reset non-file");
|
||
return FALSE;
|
||
}
|
||
iZlast_tx_data_packet = -1; /* trigger ZDATA */
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO,
|
||
"fzsend_data: Seeking to %ld",
|
||
(long) (wpZrxpos - wpZtxstart));
|
||
if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) {
|
||
ulog (LOG_ERROR, "seek: %s", strerror (errno));
|
||
return FALSE;
|
||
}
|
||
break;
|
||
default:
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* If we've reached the maximum transmit window size, let the
|
||
* receiver catch up ...
|
||
*
|
||
* I use (cZtx_window - 2048) to play it safe.
|
||
*/
|
||
|
||
while (wpZtxpos - wpZlrxpos >= cZtx_window - 2048) {
|
||
if (iZlast_tx_data_packet != ZCRCQ) {
|
||
if (!fzsend_data_packet (qdaemon, zdata, (size_t) 0,
|
||
iZlast_tx_data_packet = ZCRCQ,
|
||
TRUE))
|
||
return FALSE;
|
||
}
|
||
/*
|
||
* FIXME: I'd rather not call ffileseek() in this file. When we
|
||
* start buffering the outgoing data, the following
|
||
* ffileseek() will disappear.
|
||
*/
|
||
switch (getinsync (qdaemon, TRUE)) {
|
||
case ZACK:
|
||
break;
|
||
case ZRPOS:
|
||
if (qTsend == NULL
|
||
|| ! ffileisopen (qTsend->e)) {
|
||
ulog (LOG_ERROR, "Can't reset non-file");
|
||
return FALSE;
|
||
}
|
||
iZlast_tx_data_packet = -1; /* trigger ZDATA */
|
||
DEBUG_MESSAGE1 (DEBUG_PROTO,
|
||
"fzsend_data: Seeking to %ld",
|
||
(long) (wpZrxpos - wpZtxstart));
|
||
if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) {
|
||
ulog (LOG_ERROR, "seek: %s", strerror (errno));
|
||
return FALSE;
|
||
}
|
||
break;
|
||
default:
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Process existing data ...
|
||
*/
|
||
|
||
static boolean
|
||
fzprocess(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int c,ch;
|
||
|
||
while (fzreceive_ready ()) {
|
||
READCHAR (qdaemon, ch, 1);
|
||
switch (ch) {
|
||
case ZPAD:
|
||
/* see if we're detecting ZRPOS packets quickly */
|
||
DEBUG_MESSAGE0 (DEBUG_PROTO,
|
||
"fzprocess: possible ZRPOS packet");
|
||
/* We just ate the ZPAD char that getinsync
|
||
expects, so put it back. */
|
||
iPrecstart = ((iPrecstart + CRECBUFLEN - 1)
|
||
% CRECBUFLEN);
|
||
c = getinsync (qdaemon, TRUE);
|
||
if (c == ZACK)
|
||
break;
|
||
/* FIXME: sz does a TCFLSH here */
|
||
#if 0 /* FIXME: Not sure if this is needed, or where to put it. */
|
||
/* ZCRCE - dinna wanna starta ping-pong game */
|
||
if (!fzsend_data_packet (qdaemon, zZtx_packet_buf,
|
||
0, ZCRCE, TRUE))
|
||
return FALSE;
|
||
#endif
|
||
if (c == ZRPOS) {
|
||
if (qTsend == NULL
|
||
|| ! ffileisopen (qTsend->e)) {
|
||
ulog (LOG_ERROR,
|
||
"Attempt to back up non-file");
|
||
return FALSE;
|
||
}
|
||
if (! ffileseek (qTsend->e,
|
||
wpZrxpos - wpZtxstart)) {
|
||
ulog (LOG_ERROR,
|
||
"seek: %s", strerror (errno));
|
||
return FALSE;
|
||
}
|
||
iZlast_tx_data_packet = -1; /* trigger ZDATA */
|
||
break; /* not returning is intentional */
|
||
}
|
||
return FALSE;
|
||
case XOFF:
|
||
case XOFF | 0200:
|
||
READCHAR (qdaemon, ch, XON_WAIT);
|
||
break;
|
||
case CR:
|
||
break;
|
||
default:
|
||
iZjunk_count++;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Wait for data to come in.
|
||
*
|
||
* This continues processing until a complete file or command has been
|
||
* received.
|
||
*/
|
||
|
||
boolean
|
||
fzwait(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int c,cerr,rxcount;
|
||
boolean fexit;
|
||
achdrval_t rx_hdr;
|
||
|
||
if (!fzstart_rx ())
|
||
return FALSE;
|
||
|
||
cerr = cZretries;
|
||
|
||
goto nxthdr;
|
||
|
||
for (;;) {
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZRPOS,
|
||
hvzencode_data_hdr (wpZrxbytes), FALSE))
|
||
return FALSE;
|
||
nxthdr:
|
||
c = izrecv_hdr (qdaemon, rx_hdr);
|
||
|
||
switch (c) {
|
||
case ZM_TIMEOUT:
|
||
case ZNAK:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR, "fzwait: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
continue;
|
||
case ZM_ERROR:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR, "fzwait: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
/*fport_break ();*/
|
||
continue;
|
||
case ZM_RCDO:
|
||
case ZFIN:
|
||
return FALSE;
|
||
case ZRPOS:
|
||
case ZACK:
|
||
goto nxthdr; /* ignore, partner is out of sync */
|
||
case ZDATA: {
|
||
winpos_t rx_bytes;
|
||
|
||
zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO,
|
||
"fzwait: bytes(us,them) 0x%lx,0x%lx",
|
||
wpZrxbytes, rx_bytes);
|
||
if (rx_bytes != wpZrxbytes) {
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"fzwait: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
(void) zrdat32 (qdaemon, zZrx_packet_buf,
|
||
1024, &rxcount);
|
||
/*fport_break ();*/
|
||
/*
|
||
* FIXME: Seems to me we should ignore this one
|
||
* and go for a timeout, the theory being
|
||
* that the appropriate ZRPOS has already
|
||
* been sent. We're obviously out of sync.
|
||
* /dje 92Mar10
|
||
*/
|
||
continue; /* goto nxthdr? */
|
||
}
|
||
moredata:
|
||
/*
|
||
* Do not call fgot_data() with (rxcount == 0) if it's
|
||
* not ZCRCF. fgot_data() will erroneously think this
|
||
* is the end of the message.
|
||
*/
|
||
c = zrdat32 (qdaemon, zZrx_packet_buf, 1024,
|
||
&rxcount);
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING(DEBUG_PROTO)) {
|
||
const char *msg;
|
||
|
||
if (c < 0) {
|
||
msg = ZZHEADER_NAME(c);
|
||
} else {
|
||
switch (c) {
|
||
case GOTCRCW: msg = "ZCRCW"; break;
|
||
case GOTCRCG: msg = "ZCRCG"; break;
|
||
case GOTCRCQ: msg = "ZCRCQ"; break;
|
||
case GOTCRCE: msg = "ZCRCE"; break;
|
||
case GOTCRCF: msg = "ZCRCF"; break;
|
||
default : msg = NULL; break;
|
||
}
|
||
}
|
||
if (msg != NULL)
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO,
|
||
"fzwait: zrdat32: %s, %d bytes",
|
||
msg, rxcount);
|
||
else
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO,
|
||
"fzwait: zrdat32: %d, %d bytes",
|
||
c, rxcount);
|
||
}
|
||
#endif
|
||
switch (c) {
|
||
case ZM_ERROR: /* CRC error */
|
||
cZerrors++;
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"fzwait: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
/*fport_break ();*/
|
||
continue;
|
||
case ZM_TIMEOUT:
|
||
cZtimeouts++;
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"fzwait: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
continue;
|
||
case ZM_RCDO:
|
||
return FALSE;
|
||
case GOTCRCW:
|
||
iZlast_rx_data_packet = ZCRCW;
|
||
cerr = cZretries;
|
||
if (rxcount != 0
|
||
&& !fgot_data (qdaemon, zZrx_packet_buf,
|
||
(size_t) rxcount,
|
||
(const char *) NULL,
|
||
(size_t) 0,
|
||
-1, -1, (long) -1,
|
||
TRUE, &fexit))
|
||
return FALSE;
|
||
wpZrxbytes += rxcount;
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
|
||
hvzencode_data_hdr (wpZrxbytes),
|
||
FALSE))
|
||
return FALSE;
|
||
if (! fsend_data (qdaemon->qconn, &xon,
|
||
(size_t) 1, FALSE))
|
||
return FALSE;
|
||
goto nxthdr;
|
||
case GOTCRCQ:
|
||
iZlast_rx_data_packet = ZCRCQ;
|
||
cerr = cZretries;
|
||
if (rxcount != 0
|
||
&& !fgot_data (qdaemon, zZrx_packet_buf,
|
||
(size_t) rxcount,
|
||
(const char *) NULL,
|
||
(size_t) 0,
|
||
-1, -1, (long) -1,
|
||
TRUE, &fexit))
|
||
return FALSE;
|
||
wpZrxbytes += rxcount;
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
|
||
hvzencode_data_hdr (wpZrxbytes),
|
||
FALSE))
|
||
return FALSE;
|
||
goto moredata;
|
||
case GOTCRCG:
|
||
iZlast_rx_data_packet = ZCRCG;
|
||
cerr = cZretries;
|
||
if (rxcount != 0
|
||
&& !fgot_data (qdaemon, zZrx_packet_buf,
|
||
(size_t) rxcount,
|
||
(const char *) NULL,
|
||
(size_t) 0,
|
||
-1, -1, (long) -1,
|
||
TRUE, &fexit))
|
||
return FALSE;
|
||
wpZrxbytes += rxcount;
|
||
goto moredata;
|
||
case GOTCRCE:
|
||
iZlast_rx_data_packet = ZCRCE;
|
||
cerr = cZretries;
|
||
if (rxcount != 0
|
||
&& !fgot_data (qdaemon, zZrx_packet_buf,
|
||
(size_t) rxcount,
|
||
(const char *) NULL,
|
||
(size_t) 0,
|
||
-1, -1, (long) -1,
|
||
TRUE, &fexit))
|
||
return FALSE;
|
||
wpZrxbytes += rxcount;
|
||
goto nxthdr;
|
||
case GOTCRCF:
|
||
iZlast_rx_data_packet = ZCRCF;
|
||
/*
|
||
* fzfinish_rx() must be called before
|
||
* fgot_data() because fgot_data() will send
|
||
* out a UUCP-command but the sender won't be
|
||
* ready for it until it receives our final
|
||
* ZACK.
|
||
*/
|
||
cerr = cZretries;
|
||
wpZrxbytes += rxcount;
|
||
if (!fzfinish_rx (qdaemon))
|
||
return FALSE;
|
||
if (!fgot_data (qdaemon, zZrx_packet_buf,
|
||
(size_t) rxcount,
|
||
(const char *) NULL,
|
||
(size_t) 0, -1, -1,
|
||
(long) -1, TRUE, &fexit))
|
||
return FALSE;
|
||
/*
|
||
* FIXME: Examine <fexit>?
|
||
* Or maybe ensure it's TRUE?
|
||
*/
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
default:
|
||
ulog (LOG_FATAL, "fzwait: received header %s",
|
||
ZZHEADER_NAME(c));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* File level routine. Called when initiating/terminating file transfers.
|
||
*
|
||
* When starting to send a file: (TRUE, TRUE, cbytes)
|
||
* When starting to receive a file: (TRUE, FALSE, -1)
|
||
* When send EOF, check resend: (FALSE, TRUE, -1)
|
||
* When receive EOF, check re-receive: (FALSE, FALSE, -1)
|
||
*/
|
||
|
||
boolean
|
||
fzfile(qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
|
||
struct sdaemon *qdaemon;
|
||
struct stransfer *qtrans;
|
||
boolean fstart;
|
||
boolean fsend;
|
||
long cbytes;
|
||
boolean *pfhandled;
|
||
{
|
||
long iredo;
|
||
|
||
*pfhandled = FALSE;
|
||
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO, "fzfile: fstart=%d, fsend=%d", fstart,
|
||
fsend);
|
||
|
||
if (fsend) {
|
||
if (fstart)
|
||
return fzstart_tx ();
|
||
if (! fzfinish_tx (qdaemon, &iredo))
|
||
return FALSE;
|
||
if (iredo >= 0) {
|
||
if (! ffileisopen (qtrans->e)) {
|
||
ulog (LOG_ERROR,
|
||
"Attempt to back up non-file");
|
||
return FALSE;
|
||
}
|
||
if (! ffileseek (qtrans->e, iredo)) {
|
||
ulog (LOG_ERROR,
|
||
"seek: %s", strerror (errno));
|
||
return FALSE;
|
||
}
|
||
*pfhandled = TRUE;
|
||
qtrans->fsendfile = TRUE;
|
||
return fqueue_send (qdaemon, qtrans);
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/****************************************************************************/
|
||
|
||
|
||
#if 0 /* not used, we only use 32 bit crc's */
|
||
/*
|
||
* crctab calculated by Mark G. Mendel, Network Systems Corporation
|
||
*/
|
||
|
||
static unsigned short crctab[256] = {
|
||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||
};
|
||
#endif /* crctab */
|
||
|
||
/*
|
||
* Copyright (C) 1986 Gary S. Brown. You may use this program, or
|
||
* code or tables extracted from it, as desired without restriction.
|
||
*/
|
||
|
||
/* First, the polynomial itself and its table of feedback terms. The */
|
||
/* polynomial is */
|
||
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
|
||
/* Note that we take it "backwards" and put the highest-order term in */
|
||
/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
|
||
/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
|
||
/* the MSB being 1. */
|
||
|
||
/* Note that the usual hardware shift register implementation, which */
|
||
/* is what we're using (we're merely optimizing it by doing eight-bit */
|
||
/* chunks at a time) shifts bits into the lowest-order term. In our */
|
||
/* implementation, that means shifting towards the right. Why do we */
|
||
/* do it this way? Because the calculated CRC must be transmitted in */
|
||
/* order from highest-order term to lowest-order term. UARTs transmit */
|
||
/* characters in order from LSB to MSB. By storing the CRC this way, */
|
||
/* we hand it to the UART in the order low-byte to high-byte; the UART */
|
||
/* sends each low-bit to hight-bit; and the result is transmission bit */
|
||
/* by bit from highest- to lowest-order term without requiring any bit */
|
||
/* shuffling on our part. Reception works similarly. */
|
||
|
||
/* The feedback terms table consists of 256, 32-bit entries. Notes: */
|
||
/* */
|
||
/* The table can be generated at runtime if desired; code to do so */
|
||
/* is shown later. It might not be obvious, but the feedback */
|
||
/* terms simply represent the results of eight shift/xor opera- */
|
||
/* tions for all combinations of data and CRC register values. */
|
||
/* */
|
||
/* The values must be right-shifted by eight bits by the "updcrc" */
|
||
/* logic; the shift must be unsigned (bring in zeroes). On some */
|
||
/* hardware you could probably optimize the shift in assembler by */
|
||
/* using byte-swap instructions. */
|
||
|
||
static unsigned long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
|
||
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,
|
||
0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
|
||
0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
|
||
0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
|
||
0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
|
||
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
|
||
0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,
|
||
0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
|
||
0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
|
||
0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
|
||
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,
|
||
0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
|
||
0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,
|
||
0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
|
||
0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
|
||
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
|
||
0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,
|
||
0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
|
||
0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,
|
||
0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
|
||
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
|
||
0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
|
||
0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,
|
||
0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
|
||
0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
|
||
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
|
||
0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
|
||
0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
|
||
0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L,
|
||
0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
|
||
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,
|
||
0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
|
||
0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
|
||
0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
|
||
0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
|
||
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
|
||
0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL,
|
||
0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
|
||
0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
|
||
0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
|
||
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L,
|
||
0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
|
||
0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,
|
||
0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
|
||
0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
|
||
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
|
||
0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,
|
||
0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
|
||
0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,
|
||
0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
|
||
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
|
||
0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
|
||
0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,
|
||
0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
|
||
0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
|
||
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
|
||
0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
|
||
0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
|
||
0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,
|
||
0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
|
||
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,
|
||
0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
|
||
0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
|
||
0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
|
||
};
|
||
|
||
/*
|
||
* updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
|
||
* NOTE: First argument must be in range 0 to 255.
|
||
* Second argument is referenced twice.
|
||
*
|
||
* Programmers may incorporate any or all code into their programs,
|
||
* giving proper credit within the source. Publication of the
|
||
* source routines is permitted so long as proper credit is given
|
||
* to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
|
||
* Omen Technology.
|
||
*/
|
||
|
||
#define updcrc(cp, crc) (crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
|
||
|
||
#define UPDC32(b, crc) \
|
||
(crc_32_tab[((unsigned)(crc) ^ (unsigned)(b)) & 0xff] \
|
||
^ (((crc) >> 8) & 0x00ffffffL))
|
||
|
||
/****************************************************************************/
|
||
|
||
/*
|
||
* This section contains the guts of the Zmodem protocol. The intention
|
||
* is to leave as much of it alone as possible at the start. Overtime it
|
||
* will be cleaned up (EG: I'd like to clean up the naming of the globals).
|
||
* Also, Zmodem has a different coding style. Over time this will be converted
|
||
* to the Taylor UUCP coding style.
|
||
*/
|
||
|
||
/*
|
||
* Start the protocol (exchange init packets) ...
|
||
*
|
||
* UUCP can transfer files in both directions in one session. Therefore the
|
||
* init sequence is a little different.
|
||
*
|
||
* 1) ZINIT packets are exchanged
|
||
* - contains protocol version and protocol flags
|
||
* 2) ZDATA packets are exchanged
|
||
* - is intended to contain various numeric and string information
|
||
* 3) ZACK packets are exchanged
|
||
* 4) ZINITEND packets are exchanged
|
||
*/
|
||
|
||
static boolean
|
||
fzstart_proto(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int i;
|
||
achdrval_t tx_hdr,rx_hdr;
|
||
|
||
for (i = 0; i < cZstartup_retries; i++) {
|
||
stohdr (0L, tx_hdr);
|
||
tx_hdr[ZF0] = ZPROTOCOL_VERSION;
|
||
if (fZesc_ctl)
|
||
tx_hdr[ZF1] |= TX_ESCCTL;
|
||
switch (izexchange_init (qdaemon, ZINIT, tx_hdr, rx_hdr)) {
|
||
case -1: return FALSE;
|
||
case 0: continue;
|
||
case 1: break;
|
||
}
|
||
#if 0 /* can't work, but kept for documentation */
|
||
if (rx_hdr[ZF0] == 0) {
|
||
ulog (LOG_ERROR, "Old protocol version, init failed");
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
fZesc_ctl = fZesc_ctl || (rx_hdr[ZF1] & TX_ESCCTL) != 0;
|
||
|
||
stohdr (0L, tx_hdr);
|
||
switch (izexchange_init (qdaemon, ZDATA, tx_hdr, rx_hdr)) {
|
||
case -1: return FALSE;
|
||
case 0: continue;
|
||
case 1: break;
|
||
}
|
||
|
||
stohdr (0L, tx_hdr);
|
||
switch (izexchange_init (qdaemon, ZACK, tx_hdr, rx_hdr)) {
|
||
case -1: return FALSE;
|
||
case 0: continue;
|
||
case 1: break;
|
||
}
|
||
|
||
stohdr (0L, tx_hdr);
|
||
switch (izexchange_init (qdaemon, ZINITEND, tx_hdr, rx_hdr)) {
|
||
case -1: return FALSE;
|
||
case 0: continue;
|
||
case 1: break;
|
||
}
|
||
|
||
DEBUG_MESSAGE0 (DEBUG_PROTO,
|
||
"fzstart_proto: Protocol started");
|
||
return TRUE;
|
||
|
||
/* FIXME: see protg.c regarding sequencing here. */
|
||
}
|
||
|
||
ulog (LOG_ERROR, "Protocol init failed");
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* Exchange init messages. This is based on 'g'.
|
||
* See the comments concerning fgexchange_init() in protg.c.
|
||
*
|
||
* We return 1 for success, 0 for restart, -1 for comm failure (terminate).
|
||
*/
|
||
|
||
static int
|
||
izexchange_init(qdaemon, send_type, send_val, recv_val)
|
||
struct sdaemon *qdaemon;
|
||
int send_type;
|
||
achdrval_t send_val;
|
||
achdrval_t recv_val;
|
||
{
|
||
int i,recv_type,count;
|
||
|
||
for (i = 0; i < CEXCHANGE_INIT_RETRIES; i++) {
|
||
if (!fzsend_hdr (qdaemon, send_type == ZDATA ? ZBIN : ZHEX,
|
||
send_type, rclhdr (send_val), FALSE))
|
||
return -1;
|
||
|
||
/*
|
||
* The ZDATA packet is intended to contain the <Attn> string
|
||
* (eventually, if it's ever usable) and allow for anything
|
||
* else that will need to be thrown in.
|
||
*/
|
||
|
||
if (send_type == ZDATA) {
|
||
count = czbuild_data_packet (zZtx_packet_buf, "",
|
||
(size_t) 1, ZCRCF);
|
||
if (!fsend_data (qdaemon->qconn, zZtx_packet_buf,
|
||
(size_t) count, FALSE))
|
||
return -1;
|
||
}
|
||
|
||
recv_type = izrecv_hdr (qdaemon, recv_val);
|
||
|
||
switch (recv_type) {
|
||
case ZM_TIMEOUT:
|
||
case ZM_ERROR:
|
||
continue;
|
||
case ZM_RCDO:
|
||
case ZFIN:
|
||
return -1;
|
||
case ZINIT:
|
||
case ZACK:
|
||
case ZINITEND:
|
||
break;
|
||
case ZDATA:
|
||
if (zrdat32 (qdaemon, zZrx_packet_buf, 1024, &count)
|
||
== GOTCRCF)
|
||
break;
|
||
continue;
|
||
default:
|
||
continue;
|
||
}
|
||
|
||
if (recv_type == send_type)
|
||
return 1;
|
||
|
||
/*
|
||
* If the other side is farther along than we are, we have lost
|
||
* a packet. Fall immediately back to ZINIT (but don't fail
|
||
* if we are already doing ZINIT, since that would count
|
||
* against cStart_retries more than it should).
|
||
*
|
||
* FIXME: The ">" test is "<" in protg.c. Check who's right.
|
||
*/
|
||
|
||
if (recv_type > send_type && send_type != ZINIT)
|
||
return 0;
|
||
|
||
/*
|
||
* If we are sending ZINITEND and we receive an ZINIT, the
|
||
* other side has falled back (we know this because we have
|
||
* seen a ZINIT from them). Fall back ourselves to start
|
||
* the whole handshake over again.
|
||
*/
|
||
|
||
if (recv_type == ZINIT && send_type == ZINITEND)
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Shut down the protocol ...
|
||
*/
|
||
|
||
static boolean
|
||
fzshutdown_proto(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
(void) fzsend_hdr (qdaemon, ZHEX, ZFIN, 0L, FALSE);
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Reset the transmitter side for sending a new message ...
|
||
*/
|
||
|
||
static boolean
|
||
fzstart_tx()
|
||
{
|
||
iZlast_tx_data_packet = -1;
|
||
|
||
/*
|
||
* <wpZlastsync> is set to -1L to suppress ZCRCW request otherwise
|
||
* triggered by (wpZlastsync == wpZtxpos).
|
||
*/
|
||
|
||
cZblklen = 1024;
|
||
wpZlastsync = -1L;
|
||
iZbeenhereb4 = 0;
|
||
iZtleft = 0;
|
||
iZjunk_count = 0;
|
||
|
||
wpZtxpos = (wpZtxpos + 1024L) & ~1023L; /* next packet boundary */
|
||
wpZlrxpos = wpZrxpos = wpZtxpos;
|
||
|
||
wpZtxstart = wpZtxpos; /* so we can compute the "file offset" */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Finish the sending of a message ...
|
||
*
|
||
* Basically, we wait for some indication that the receiver received our last
|
||
* message. If the receiver tells us to restart from some point, we set
|
||
* *plredo to that point.
|
||
*
|
||
* FIXME: This function is a major kludge at the moment. It is taken from
|
||
* getinsync(). It is necessary because I don't yet buffer outgoing data.
|
||
* It will go away when we do (buffer outgoing data).
|
||
*/
|
||
|
||
static boolean
|
||
fzfinish_tx(qdaemon, plredo)
|
||
struct sdaemon *qdaemon;
|
||
long *plredo;
|
||
{
|
||
int c,cerr,ctimeouts;
|
||
achdrval_t rx_hdr;
|
||
winpos_t rx_bytes;
|
||
|
||
*plredo = -1;
|
||
cerr = cZretries;
|
||
ctimeouts = 0;
|
||
|
||
DEBUG_MESSAGE4 (DEBUG_PROTO,
|
||
"fzfinish_tx: txpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, rxbytes=0x%lx",
|
||
wpZtxpos, wpZrxpos, wpZlrxpos, wpZrxbytes);
|
||
|
||
for (;;) {
|
||
c = izrecv_hdr (qdaemon, rx_hdr);
|
||
|
||
switch (c) {
|
||
case ZRPOS:
|
||
wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
|
||
wpZlrxpos, wpZtxpos);
|
||
/*
|
||
* If the receiver sends a ZRPOS for the 1k block after
|
||
* the one we're currently at, we lost the final ZACK.
|
||
* We cheat and ignore this ZRPOS. Remember: the theory
|
||
* is that this entire function will go away when we
|
||
* begin buffering the outgoing data. Of course, one
|
||
* can reword the protocol definition and say this
|
||
* isn't cheating at all.
|
||
*/
|
||
if (((wpZtxpos + 1024) & ~1023) == wpZrxpos)
|
||
return TRUE;
|
||
cZbytes_resent += wpZtxpos - wpZrxpos;
|
||
wpZlrxpos = wpZtxpos = wpZrxpos;
|
||
if (wpZlastsync == wpZrxpos) {
|
||
if (++iZbeenhereb4 > 4)
|
||
if (cZblklen > 32)
|
||
cZblklen /= 2;
|
||
/* FIXME: shouldn't we reset iZbeenhereb4? */
|
||
}
|
||
wpZlastsync = wpZrxpos;
|
||
iZlast_tx_data_packet = ZCRCW; /* force a timeout */
|
||
*plredo = wpZrxpos - wpZtxstart;
|
||
return TRUE;
|
||
case ZACK:
|
||
wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
|
||
wpZlrxpos, wpZtxpos);
|
||
wpZlrxpos = wpZrxpos;
|
||
if (wpZtxpos == wpZrxpos) /* the ACK we want? */
|
||
return TRUE;
|
||
break;
|
||
case ZDATA:
|
||
/*
|
||
* We cheat here and take advantage of UUCP's current
|
||
* half duplex nature. If we get a ZDATA starting on
|
||
* the next 1k boundary, we lost the ZACK. We cheat and
|
||
* tuck it away so that izrecv_hdr() can later detect
|
||
* it. Remember: see above.
|
||
*/
|
||
zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
|
||
if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) {
|
||
iZpkt_rcvd_kludge = ZDATA;
|
||
hvZpkt_hdrval_kludge = rclhdr (rx_hdr);
|
||
return TRUE;
|
||
}
|
||
break; /* ignore, out of sync (old) */
|
||
case ZNAK:
|
||
/*
|
||
* We cheat here and take advantage of UUCP's current
|
||
* half duplex nature. If we get a ZNAK starting on
|
||
* the next 1k boundary, we lost the ZACK. We cheat and
|
||
* throw the ZNAK away. Remember: see above.
|
||
*
|
||
* On the other hand, if (rx_bytes == wpZrxbytes) then
|
||
* the other side is also in fzfinish_tx(). He must
|
||
* have lost our ZACK, so we send him another.
|
||
*/
|
||
zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
|
||
if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes)
|
||
return TRUE;
|
||
if (rx_bytes == wpZrxbytes) {
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
|
||
hvzencode_data_hdr (wpZrxbytes),
|
||
TRUE))
|
||
return FALSE;
|
||
}
|
||
break; /* ignore, out of sync (old) */
|
||
case ZFIN:
|
||
case ZM_RCDO:
|
||
return FALSE;
|
||
case ZM_TIMEOUT:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"fzfinish_tx: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
/*
|
||
* Normally the sender doesn't send NAK's for timeouts.
|
||
* We have to here because of the following scenario:
|
||
*
|
||
* - We send ZDATA/ZCRCF
|
||
* - They send ZACK (corrupted)
|
||
* - They send ZDATA/ZCRCF (corrupted)
|
||
*
|
||
* At this point, both sides are in fzfinish_tx().
|
||
* We only send ZNAK every second timeout to increase
|
||
* our timeout delay vs. our partner. This tries to
|
||
* avoid ZRPOS and ZNAK "passing in transit".
|
||
*/
|
||
if (++ctimeouts % 2 == 0)
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
|
||
hvzencode_data_hdr (wpZtxpos),
|
||
TRUE))
|
||
return FALSE;
|
||
break;
|
||
case ZM_ERROR:
|
||
default:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"fzfinish_tx: retries exhausted");
|
||
return FALSE;
|
||
}
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
|
||
hvzencode_data_hdr (wpZtxpos),
|
||
TRUE))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Initialize the receiver ...
|
||
*/
|
||
|
||
static boolean
|
||
fzstart_rx()
|
||
{
|
||
wpZrxbytes = (wpZrxbytes + 1024L) & ~1023L; /* next packet boundary */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Terminate the receiver ...
|
||
*
|
||
* Acknowledge the last packet received.
|
||
*/
|
||
|
||
static boolean
|
||
fzfinish_rx(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
DEBUG_MESSAGE0 (DEBUG_PROTO, "fzfinish_rx: message/file received");
|
||
|
||
return fzsend_hdr (qdaemon, ZHEX, ZACK,
|
||
hvzencode_data_hdr (wpZrxbytes), FALSE);
|
||
}
|
||
|
||
/*
|
||
* Send a Zmodem header to our partner ...
|
||
*/
|
||
|
||
static boolean
|
||
fzsend_hdr(qdaemon, ipkttype, ihdrtype, hdrval, fcheckreceive)
|
||
struct sdaemon *qdaemon;
|
||
int ipkttype;
|
||
int ihdrtype;
|
||
hdrval_t hdrval;
|
||
boolean fcheckreceive;
|
||
{
|
||
int cpacketlen;
|
||
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO, "fzsend_hdr: %s, data = 0x%lx",
|
||
ZZHEADER_NAME(ihdrtype), hdrval);
|
||
|
||
cpacketlen = czbuild_header (zZtx_packet_buf, ipkttype,
|
||
ihdrtype, hdrval);
|
||
|
||
#ifdef DJE_TESTING
|
||
#if 0
|
||
if (ihdrtype == ZACK && rand () % 100 < uucptest2) {
|
||
cZheaders_sent++;
|
||
return TRUE;
|
||
}
|
||
#else
|
||
if (ihdrtype == ZACK || ihdrtype == ZDATA) {
|
||
boolean fresult;
|
||
int old;
|
||
extern int uucptest,uucptest2;
|
||
|
||
old = uucptest;
|
||
uucptest = uucptest2;
|
||
cZheaders_sent++;
|
||
fresult = fsend_data (qdaemon->qconn, zZtx_packet_buf,
|
||
(size_t) cpacketlen, fcheckreceive);
|
||
uucptest = old;
|
||
return fresult;
|
||
}
|
||
#endif
|
||
#endif
|
||
cZheaders_sent++;
|
||
return fsend_data (qdaemon->qconn, zZtx_packet_buf,
|
||
(size_t) cpacketlen, fcheckreceive);
|
||
}
|
||
|
||
/*
|
||
* Send a data packet to our partner ...
|
||
* <frameend> is one of ZCRCx.
|
||
*/
|
||
|
||
static boolean
|
||
fzsend_data_packet(qdaemon, zdata, cdata, frameend, fcheckreceive)
|
||
struct sdaemon *qdaemon;
|
||
char *zdata;
|
||
size_t cdata;
|
||
int frameend;
|
||
boolean fcheckreceive;
|
||
{
|
||
int cpacketlen;
|
||
|
||
cpacketlen = czbuild_data_packet (zZtx_packet_buf, zdata, cdata,
|
||
frameend);
|
||
|
||
return fsend_data (qdaemon->qconn, zZtx_packet_buf,
|
||
(size_t) cpacketlen, fcheckreceive);
|
||
}
|
||
|
||
/*
|
||
* Build Zmodem headers ...
|
||
*
|
||
* Note that we use 32 bit CRC's for ZHEX headers.
|
||
*
|
||
* This function is a combination of zm fns: zsbhdr(), zsbh32(), and zshhdr().
|
||
*/
|
||
|
||
static int
|
||
czbuild_header(zresult, ipkttype, ihdrtype, hdrval)
|
||
char *zresult;
|
||
int ipkttype;
|
||
int ihdrtype;
|
||
hdrval_t hdrval;
|
||
{
|
||
char *p;
|
||
int i;
|
||
unsigned long crc;
|
||
achdrval_t achdrval;
|
||
|
||
p = zresult;
|
||
|
||
switch (ipkttype) {
|
||
case ZBIN:
|
||
*p++ = ZPAD;
|
||
*p++ = ZDLE;
|
||
*p++ = ZBIN;
|
||
p = zputchar (p, ihdrtype);
|
||
crc = ICRCINIT;
|
||
crc = UPDC32 (ihdrtype, crc);
|
||
stohdr (hdrval, achdrval);
|
||
for (i = 0; i < 4; i++) {
|
||
p = zputchar (p, achdrval[i]);
|
||
crc = UPDC32 (achdrval[i], crc);
|
||
}
|
||
crc = ~crc;
|
||
for (i = 0; i < 4; i++) {
|
||
p = zputchar (p, (char) crc);
|
||
crc >>= 8;
|
||
}
|
||
break;
|
||
case ZHEX: /* build hex header */
|
||
*p++ = ZPAD;
|
||
*p++ = ZPAD;
|
||
*p++ = ZDLE;
|
||
*p++ = ZHEX;
|
||
p = zputhex (p, ihdrtype);
|
||
crc = ICRCINIT;
|
||
crc = UPDC32 (ihdrtype, crc);
|
||
stohdr (hdrval, achdrval);
|
||
for (i = 0; i < 4; i++) {
|
||
p = zputhex (p, achdrval[i]);
|
||
crc = UPDC32 (achdrval[i], crc);
|
||
}
|
||
crc = ~crc;
|
||
for (i = 0; i < 4; i++) {
|
||
p = zputhex (p, (char) crc);
|
||
crc >>= 8;
|
||
}
|
||
*p++ = CR;
|
||
/*
|
||
* Uncork the remote in case a fake XOFF has stopped data flow.
|
||
*/
|
||
if (ihdrtype != ZFIN && ihdrtype != ZACK) /* FIXME: why? */
|
||
*p++ = XON;
|
||
break;
|
||
default:
|
||
ulog (LOG_FATAL, "czbuild_header: ipkttype == %d", ipkttype);
|
||
break;
|
||
}
|
||
|
||
return p - zresult;
|
||
}
|
||
|
||
/*
|
||
* Build Zmodem data packets ...
|
||
*
|
||
* This function is zsdata() and zsda32() from the zm source.
|
||
*/
|
||
|
||
static int
|
||
czbuild_data_packet(zresult, zdata, cdata, frameend)
|
||
char *zresult;
|
||
const char *zdata;
|
||
size_t cdata;
|
||
int frameend;
|
||
{
|
||
char *p;
|
||
unsigned long crc;
|
||
|
||
p = zresult;
|
||
|
||
crc = ICRCINIT;
|
||
for ( ; cdata-- != 0; zdata++) {
|
||
char c;
|
||
|
||
c = *zdata;
|
||
if (c & 0140)
|
||
*p++ = c;
|
||
else
|
||
p = zputchar (p, c);
|
||
crc = UPDC32 ((unsigned char) c, crc);
|
||
}
|
||
*p++ = ZDLE;
|
||
*p++ = frameend;
|
||
crc = UPDC32 (frameend, crc);
|
||
crc = ~crc;
|
||
for (cdata = 0; cdata < 4; cdata++) {
|
||
p = zputchar (p, (char) crc);
|
||
crc >>= 8;
|
||
}
|
||
if (frameend == ZCRCW || frameend == ZCRCE || frameend == ZCRCF) {
|
||
*p++ = CR;
|
||
*p++ = XON;
|
||
}
|
||
|
||
return p - zresult;
|
||
}
|
||
|
||
/*
|
||
* Read in a header ...
|
||
*
|
||
* This is function zgethdr() from the Zmodem source.
|
||
*/
|
||
|
||
static int
|
||
izrecv_hdr(qdaemon, hdr)
|
||
struct sdaemon *qdaemon;
|
||
achdrval_t hdr;
|
||
{
|
||
int c,cerr;
|
||
|
||
/*
|
||
* Kludge alert! If another part of the program received a packet but
|
||
* wasn't ready to handle it, it is tucked away for us to handle now.
|
||
*/
|
||
|
||
if (iZpkt_rcvd_kludge != -1) {
|
||
c = iZpkt_rcvd_kludge;
|
||
iZpkt_rcvd_kludge = -1;
|
||
stohdr (hvZpkt_hdrval_kludge, hdr);
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO,
|
||
"izrecv_hdr: queued %s, data = 0x%lx",
|
||
ZZHEADER_NAME(c), rclhdr (hdr));
|
||
cZheaders_received++;
|
||
return c;
|
||
}
|
||
|
||
cerr = cZmax_garbage; /* Max bytes before start of frame */
|
||
|
||
again:
|
||
switch (c = noxrd7 (qdaemon)) {
|
||
case ZM_TIMEOUT:
|
||
case ZM_ERROR:
|
||
case ZM_RCDO:
|
||
goto fifi;
|
||
case ZPAD: /* This is what we want */
|
||
break;
|
||
case CR: /* padding at end of previous header */
|
||
default:
|
||
if (--cerr < 0) {
|
||
c = ZM_ERROR;
|
||
goto fifi;
|
||
}
|
||
goto again;
|
||
}
|
||
|
||
splat:
|
||
switch (c = noxrd7 (qdaemon)) {
|
||
case ZPAD:
|
||
if (--cerr < 0) {
|
||
c = ZM_ERROR;
|
||
goto fifi;
|
||
}
|
||
goto splat;
|
||
case ZM_TIMEOUT:
|
||
case ZM_RCDO:
|
||
goto fifi;
|
||
case ZDLE: /* This is what we want */
|
||
break;
|
||
default:
|
||
if (--cerr < 0) {
|
||
c = ZM_ERROR;
|
||
goto fifi;
|
||
}
|
||
goto again;
|
||
}
|
||
|
||
switch (c = noxrd7 (qdaemon)) {
|
||
case ZM_TIMEOUT:
|
||
case ZM_RCDO:
|
||
goto fifi;
|
||
case ZBIN:
|
||
c = zrbhdr32 (qdaemon, hdr);
|
||
break;
|
||
case ZHEX:
|
||
c = zrhhdr (qdaemon, hdr);
|
||
break;
|
||
default:
|
||
if (--cerr < 0) {
|
||
c = ZM_ERROR;
|
||
goto fifi;
|
||
}
|
||
goto again;
|
||
}
|
||
|
||
fifi:
|
||
switch (c) {
|
||
case ZM_TIMEOUT:
|
||
cZtimeouts++;
|
||
break;
|
||
case ZM_ERROR:
|
||
cZerrors++;
|
||
break;
|
||
case ZM_RCDO:
|
||
break;
|
||
default:
|
||
cZheaders_received++;
|
||
break;
|
||
}
|
||
DEBUG_MESSAGE2 (DEBUG_PROTO, "izrecv_hdr: %s, data = 0x%lx",
|
||
ZZHEADER_NAME(c), rclhdr (hdr));
|
||
|
||
return c;
|
||
}
|
||
|
||
/*
|
||
* Receive a binary style header (type and position) with 32 bit FCS ...
|
||
*/
|
||
|
||
static int
|
||
zrbhdr32(qdaemon, hdr)
|
||
struct sdaemon *qdaemon;
|
||
achdrval_t hdr;
|
||
{
|
||
int c,i,type;
|
||
unsigned long crc;
|
||
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
return c;
|
||
type = c;
|
||
crc = ICRCINIT;
|
||
crc = UPDC32 (c, crc);
|
||
|
||
for (i = 0; i < 4; i++) {
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
return c;
|
||
crc = UPDC32 (c, crc);
|
||
hdr[i] = (char) c;
|
||
}
|
||
for (i = 0; i < 4; i++) {
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
return c;
|
||
crc = UPDC32 (c, crc);
|
||
}
|
||
if (crc != IHDRCRC)
|
||
return ZM_ERROR;
|
||
|
||
return type;
|
||
}
|
||
|
||
/*
|
||
* Receive a hex style header (type and position) ...
|
||
*/
|
||
|
||
static int
|
||
zrhhdr(qdaemon, hdr)
|
||
struct sdaemon *qdaemon;
|
||
achdrval_t hdr;
|
||
{
|
||
int c,i,type;
|
||
unsigned long crc;
|
||
|
||
if ((c = zgethex (qdaemon)) < 0)
|
||
return c;
|
||
type = c;
|
||
crc = ICRCINIT;
|
||
crc = UPDC32 (c, crc);
|
||
|
||
for (i = 0; i < 4; i++) {
|
||
if ((c = zgethex (qdaemon)) < 0)
|
||
return c;
|
||
crc = UPDC32 (c, crc);
|
||
hdr[i] = (char) c;
|
||
}
|
||
for (i = 0; i < 4; i++) {
|
||
if ((c = zgethex (qdaemon)) < 0)
|
||
return c;
|
||
crc = UPDC32 (c, crc);
|
||
}
|
||
if (crc != IHDRCRC)
|
||
return ZM_ERROR;
|
||
|
||
return type;
|
||
}
|
||
|
||
/*
|
||
* Receive a data packet ...
|
||
*/
|
||
|
||
static int
|
||
zrdat32(qdaemon, buf, length, iprxcount)
|
||
struct sdaemon *qdaemon;
|
||
char *buf;
|
||
int length;
|
||
int *iprxcount;
|
||
{
|
||
int c,d;
|
||
unsigned long crc;
|
||
char *end;
|
||
|
||
crc = ICRCINIT;
|
||
*iprxcount = 0;
|
||
end = buf + length;
|
||
while (buf <= end) {
|
||
if ((c = zdlread (qdaemon)) & ~0377) {
|
||
crcfoo:
|
||
switch (c) {
|
||
case GOTCRCE:
|
||
case GOTCRCG:
|
||
case GOTCRCQ:
|
||
case GOTCRCW:
|
||
case GOTCRCF:
|
||
d = c;
|
||
c &= 0377;
|
||
crc = UPDC32 (c, crc);
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
goto crcfoo;
|
||
crc = UPDC32 (c, crc);
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
goto crcfoo;
|
||
crc = UPDC32 (c, crc);
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
goto crcfoo;
|
||
crc = UPDC32 (c, crc);
|
||
if ((c = zdlread (qdaemon)) & ~0377)
|
||
goto crcfoo;
|
||
crc = UPDC32 (c, crc);
|
||
if (crc != IHDRCRC)
|
||
return ZM_ERROR;
|
||
*iprxcount = length - (end - buf);
|
||
return d;
|
||
case ZM_TIMEOUT:
|
||
case ZM_RCDO:
|
||
return c;
|
||
default:
|
||
return ZM_ERROR;
|
||
}
|
||
}
|
||
*buf++ = (char) c;
|
||
crc = UPDC32 (c, crc);
|
||
}
|
||
|
||
return ZM_ERROR; /* bad packet, too long */
|
||
}
|
||
|
||
/*
|
||
* Respond to receiver's complaint, get back in sync with receiver ...
|
||
*/
|
||
|
||
static int
|
||
getinsync(qdaemon, flag)
|
||
struct sdaemon *qdaemon;
|
||
boolean flag;
|
||
{
|
||
int c,cerr;
|
||
achdrval_t rx_hdr;
|
||
|
||
cerr = cZretries;
|
||
|
||
for (;;) {
|
||
c = izrecv_hdr (qdaemon, rx_hdr);
|
||
|
||
switch (c) {
|
||
case ZRPOS:
|
||
wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
|
||
wpZlrxpos, wpZtxpos);
|
||
cZbytes_resent += wpZtxpos - wpZrxpos;
|
||
wpZlrxpos = wpZtxpos = wpZrxpos;
|
||
if (wpZlastsync == wpZrxpos) {
|
||
if (++iZbeenhereb4 > 4)
|
||
if (cZblklen > 32)
|
||
cZblklen /= 2;
|
||
/* FIXME: shouldn't we reset iZbeenhereb4? */
|
||
}
|
||
wpZlastsync = wpZrxpos;
|
||
return ZRPOS;
|
||
case ZACK:
|
||
wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
|
||
wpZlrxpos, wpZtxpos);
|
||
wpZlrxpos = wpZrxpos;
|
||
if (flag || wpZtxpos == wpZrxpos)
|
||
return ZACK;
|
||
break;
|
||
case ZNAK: {
|
||
winpos_t rx_bytes;
|
||
/*
|
||
* Our partner is in fzfinish_tx() and is waiting
|
||
* for ZACK ...
|
||
*/
|
||
zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
|
||
if (rx_bytes == wpZrxbytes) {
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
|
||
hvzencode_data_hdr (wpZrxbytes),
|
||
TRUE))
|
||
return FALSE;
|
||
}
|
||
break;
|
||
}
|
||
case ZFIN:
|
||
case ZM_RCDO:
|
||
return c;
|
||
case ZM_TIMEOUT:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"getinsync: retries exhausted");
|
||
return ZM_ERROR;
|
||
}
|
||
break; /* sender doesn't send ZNAK for timeout */
|
||
case ZM_ERROR:
|
||
default:
|
||
if (--cerr < 0) {
|
||
ulog (LOG_ERROR,
|
||
"getinsync: retries exhausted");
|
||
return ZM_ERROR;
|
||
}
|
||
if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
|
||
hvzencode_data_hdr (wpZtxpos),
|
||
TRUE))
|
||
return ZM_ERROR;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Send a byte as two hex digits ...
|
||
*/
|
||
|
||
static char *
|
||
zputhex(p, ch)
|
||
char *p;
|
||
int ch;
|
||
{
|
||
static char digits[] = "0123456789abcdef";
|
||
|
||
*p++ = digits[(ch & 0xF0) >> 4];
|
||
*p++ = digits[ch & 0xF];
|
||
return p;
|
||
}
|
||
|
||
/*
|
||
* Send character c with ZMODEM escape sequence encoding ...
|
||
*
|
||
* Escape XON, XOFF.
|
||
* FIXME: Escape CR following @ (Telenet net escape) ... disabled for now
|
||
* Will need to put back references to <lastsent>.
|
||
*/
|
||
|
||
static char *
|
||
zputchar(p, ch)
|
||
char *p;
|
||
int ch;
|
||
{
|
||
char c = ch;
|
||
|
||
/* Quick check for non control characters */
|
||
|
||
if (c & 0140) {
|
||
*p++ = c;
|
||
} else {
|
||
switch (c & 0377) {
|
||
case ZDLE:
|
||
*p++ = ZDLE;
|
||
*p++ = c ^ 0100;
|
||
break;
|
||
case CR:
|
||
#if 0
|
||
if (!fZesc_ctl && (lastsent & 0177) != '@')
|
||
goto sendit;
|
||
#endif
|
||
/* fall through */
|
||
case 020: /* ^P */
|
||
case XON:
|
||
case XOFF:
|
||
*p++ = ZDLE;
|
||
c ^= 0100;
|
||
/*sendit:*/
|
||
*p++ = c;
|
||
break;
|
||
default:
|
||
if (fZesc_ctl && !(c & 0140)) {
|
||
*p++ = ZDLE;
|
||
c ^= 0100;
|
||
}
|
||
*p++ = c;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return p;
|
||
}
|
||
|
||
/*
|
||
* Decode two lower case hex digits into an 8 bit byte value ...
|
||
*/
|
||
|
||
static int
|
||
zgethex(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int c,n;
|
||
|
||
if ((c = noxrd7 (qdaemon)) < 0)
|
||
return c;
|
||
n = c - '0';
|
||
if (n > 9)
|
||
n -= ('a' - ':');
|
||
if (n & ~0xF)
|
||
return ZM_ERROR;
|
||
if ((c = noxrd7 (qdaemon)) < 0)
|
||
return c;
|
||
c -= '0';
|
||
if (c > 9)
|
||
c -= ('a' - ':');
|
||
if (c & ~0xF)
|
||
return ZM_ERROR;
|
||
c += (n << 4);
|
||
|
||
return c;
|
||
}
|
||
|
||
/*
|
||
* Read a byte, checking for ZMODEM escape encoding ...
|
||
*/
|
||
|
||
static int
|
||
zdlread(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int c;
|
||
|
||
again:
|
||
READCHAR (qdaemon, c, cZtimeout);
|
||
if (c < 0)
|
||
return c;
|
||
if (c & 0140) /* quick check for non control characters */
|
||
return c;
|
||
switch (c) {
|
||
case ZDLE:
|
||
break;
|
||
case XON:
|
||
goto again;
|
||
case XOFF:
|
||
READCHAR (qdaemon, c, XON_WAIT);
|
||
goto again;
|
||
default:
|
||
if (fZesc_ctl && !(c & 0140))
|
||
goto again;
|
||
return c;
|
||
}
|
||
|
||
again2:
|
||
READCHAR (qdaemon, c, cZtimeout);
|
||
if (c < 0)
|
||
return c;
|
||
switch (c) {
|
||
case ZCRCE:
|
||
case ZCRCG:
|
||
case ZCRCQ:
|
||
case ZCRCW:
|
||
case ZCRCF:
|
||
return c | GOTOR;
|
||
case ZRUB0: /* FIXME: This is never generated. */
|
||
return 0177;
|
||
case ZRUB1: /* FIXME: This is never generated. */
|
||
return 0377;
|
||
case XON:
|
||
goto again2;
|
||
case XOFF:
|
||
READCHAR (qdaemon, c, XON_WAIT);
|
||
goto again2;
|
||
default:
|
||
if (fZesc_ctl && !(c & 0140))
|
||
goto again2; /* FIXME: why again2? */
|
||
if ((c & 0140) == 0100)
|
||
return c ^ 0100;
|
||
break;
|
||
}
|
||
|
||
return ZM_ERROR;
|
||
}
|
||
|
||
/*
|
||
* Read a character from the modem line with timeout ...
|
||
* Eat parity bit, XON and XOFF characters.
|
||
*/
|
||
|
||
static int
|
||
noxrd7(qdaemon)
|
||
struct sdaemon *qdaemon;
|
||
{
|
||
int c;
|
||
|
||
for (;;) {
|
||
READCHAR (qdaemon, c, cZtimeout);
|
||
if (c < 0)
|
||
return c;
|
||
switch (c &= 0177) {
|
||
case XON:
|
||
continue;
|
||
case XOFF:
|
||
READCHAR (qdaemon, c, XON_WAIT);
|
||
continue;
|
||
case CR:
|
||
case ZDLE:
|
||
return c;
|
||
default:
|
||
if (fZesc_ctl && !(c & 0140))
|
||
continue;
|
||
return c;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Read a character from the receive buffer, or from the line if empty ...
|
||
*
|
||
* <timeout> is in seconds (maybe make it tenths of seconds like in Zmodem?)
|
||
*/
|
||
|
||
static int
|
||
realreadchar(qdaemon, timeout)
|
||
struct sdaemon *qdaemon;
|
||
int timeout;
|
||
{
|
||
int c;
|
||
|
||
if ((c = breceive_char (qdaemon->qconn, timeout, TRUE)) >= 0)
|
||
return c;
|
||
|
||
switch (c) {
|
||
case -1:
|
||
return ZM_TIMEOUT;
|
||
case -2:
|
||
return ZM_RCDO;
|
||
}
|
||
|
||
ulog (LOG_FATAL, "realreadchar: breceive_char() returned %d", c);
|
||
return ZM_ERROR;
|
||
}
|
||
|
||
|
||
/*
|
||
* Check if the receive channel has any characters in it.
|
||
*
|
||
* At present we can only test the receive buffer. No mechanism is available
|
||
* to go to the hardware. This should not be a problem though, as long as all
|
||
* appropriate calls to fsend_data() set <fdoread> to TRUE.
|
||
*/
|
||
|
||
static boolean
|
||
fzreceive_ready()
|
||
{
|
||
return iPrecstart != iPrecend;
|
||
}
|
||
|
||
/*
|
||
* Store integer value in an achdrval_t ...
|
||
*/
|
||
|
||
static void
|
||
stohdr(val, hdr)
|
||
hdrval_t val;
|
||
achdrval_t hdr;
|
||
{
|
||
hdr[ZP0] = (char) val;
|
||
hdr[ZP1] = (char) (val >> 8);
|
||
hdr[ZP2] = (char) (val >> 16);
|
||
hdr[ZP3] = (char) (val >> 24);
|
||
}
|
||
|
||
/*
|
||
* Recover an integer from a header ...
|
||
*/
|
||
|
||
static hdrval_t
|
||
rclhdr(hdr)
|
||
achdrval_t hdr;
|
||
{
|
||
hdrval_t v;
|
||
|
||
v = hdr[ZP3] & 0377;
|
||
v = (v << 8) | (hdr[ZP2] & 0377);
|
||
v = (v << 8) | (hdr[ZP1] & 0377);
|
||
v = (v << 8) | (hdr[ZP0] & 0377);
|
||
|
||
return v;
|
||
}
|
||
|
||
/*
|
||
* Encode a <hdrval_t> from the byte count ...
|
||
*
|
||
* We use to store the byte count / 32 and a message sequence number which
|
||
* made this function very useful. Don't remove it.
|
||
* FIXME: Well, maybe remove it later.
|
||
*/
|
||
|
||
static hdrval_t
|
||
hvzencode_data_hdr(cbytes)
|
||
winpos_t cbytes;
|
||
{
|
||
return (hdrval_t) cbytes;
|
||
}
|
||
|
||
/*
|
||
* Decode a <hdrval_t> into a byte count ...
|
||
*
|
||
* We use to store the byte count / 32 and a message sequence number which
|
||
* made this function very useful. Don't remove it.
|
||
* FIXME: Well, maybe remove it later.
|
||
*/
|
||
|
||
static void
|
||
zdecode_data_hdr(hdrval, pcbytes)
|
||
hdrval_t hdrval;
|
||
winpos_t *pcbytes;
|
||
{
|
||
*pcbytes = hdrval;
|
||
}
|
||
|
||
/*
|
||
* Update <wpZrxpos> from the received data header value ...
|
||
*
|
||
* FIXME: Here is where we'd handle wrapping around at 4 gigabytes.
|
||
*/
|
||
|
||
static winpos_t
|
||
lzupdate_rxpos(rx_hdr, rxpos, lrxpos, txpos)
|
||
achdrval_t rx_hdr;
|
||
winpos_t rxpos,lrxpos,txpos;
|
||
{
|
||
winpos_t rx_pktpos;
|
||
|
||
zdecode_data_hdr (rclhdr (rx_hdr), &rx_pktpos);
|
||
|
||
DEBUG_MESSAGE4 (DEBUG_PROTO,
|
||
"lzupdate_rxpos: rx_pktpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, txpos=0x%lx",
|
||
rx_pktpos, rxpos, lrxpos, txpos);
|
||
|
||
/*
|
||
* Check if <rx_pktpos> valid. It could be old.
|
||
*/
|
||
|
||
if (rx_pktpos < wpZlrxpos
|
||
|| rx_pktpos > ((wpZtxpos + 1024L) & ~1023L))
|
||
return rxpos;
|
||
|
||
return rx_pktpos;
|
||
}
|