/* 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
* ZDATA (ZCRCF) --> <-- ZDATA (ZCRCF)
* 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
* 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
* 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
* *** 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):
* A typical message sequence is (master receiving file from slave):
*****************************************************************************
* 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
*****************************************************************************
* Early attempts, completely rewritten later.
* Beta test sent to Ian for analysis 92Apr18.
*****************************************************************************
* 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.
* Zmodem controlled file transfer. This was more of a "plug Z
* 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.
const char protz_rcsid
[] = "$Id: protz.c,v 1.1 1993/08/04 19:36:25 jtc Exp $";
#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 */
* 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 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 ZP0 0 /* Low order 8 bits of position */
#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_RCDO (-3) /* Carrier Lost */
#define XON_WAIT 10 /* seconds */
* 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. */
#define IHDRCRC 0xDEBB20E3UL
#define IHDRCRC ((unsigned long) 0xDEBB20E3L)
#define CPACKBUFSIZE (CFRAMELEN + 2 * CPACKETSIZE + CSUFFIXLEN + 42 /*slop*/)
typedef unsigned char achdrval_t
[4];
typedef unsigned long hdrval_t
;
typedef unsigned long winpos_t
;
* FIXME: <cZrx_buf_len> isn't used yet. It may not be needed.
#define CSTARTUP_RETRIES 4
#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
,
* 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
;
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 */
* 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
;
static const char *azZframe_types
[] = {
#define FTNUMBER (sizeof(azZframe_types) / sizeof(char *))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define ZZHEADER_NAME(itype) \
azZframe_types[min((itype) + FTOFFSET, FTNUMBER - 1)]
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
,
static boolean fzsend_data_packet
P((struct sdaemon
*qdaemon
, char *zdata
,
size_t cdata
, int frameend
,
static int czbuild_header
P((char *zresult
, int ipkttype
, int ihdrtype
,
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
,
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))))
/************************************************************************/
*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
))
* 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
if (cZtx_window
% 1024 != 0 ||
cZtx_window
< 4096 || cZtx_window
> 65536 ||
"fzstart: cZtx_window not one of 4096, 8192, 16384, 32768, 65536");
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;
* 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
;
extern int uucptest
,uucptest2
,uucptestseed
;
f
= fopen ("/usr/local/src/bin/uucp/uucptest", "r");
&uucptestseed
, &uucptest
, &uucptest2
);
* Fire up the protocol (exchange init messages) ...
if (!fzstart_proto (qdaemon
))
(void) fzshutdown_proto (qdaemon
);
xfree ((pointer
) zZtx_buf
);
xfree ((pointer
) zZtx_packet_buf
);
xfree ((pointer
) zZrx_packet_buf
);
* Print some informative statistics ...
* I use the word "messages" here instead of "headers" because the
"Protocol 'a' messages: sent %lu, received %lu",
cZheaders_sent
, cZheaders_received
);
"Protocol 'a' packets: sent %lu, received %lu",
wpZtxpos
/ 1024, wpZrxbytes
/ 1024);
if (cZbytes_resent
!= 0 || cZtimeouts
!= 0 || cZerrors
!= 0)
"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
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;
* 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?
fzsendcmd(qdaemon
, z
, ilocal
, iremote
)
DEBUG_MESSAGE1 (DEBUG_PROTO
, "fzsendcmd: sending command %s", z
);
if (!fzstart_tx ()) /* must be called before zzgetspace() */
if ((zbuf
= zzgetspace (qdaemon
, &n
)) == NULL
)
ulog (LOG_FATAL
, "fzsendcmd: clen > n");
if (!fzsend_data (qdaemon
, zbuf
, clen
, TRUE
))
if (!fzfinish_tx (qdaemon
, &lredo
))
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.
zzgetspace(qdaemon
, pclen
)
* Send a block of data ...
* If (cdata == 0) then the end of the file has been reached.
fzsenddata(qdaemon
, zdata
, cdata
, ilocal
, iremote
, ipos
)
DEBUG_MESSAGE1 (DEBUG_PROTO
, "fzsenddata: %d bytes", cdata
);
if (! fzsend_data (qdaemon
, zdata
, cdata
, cdata
== 0))
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
extern struct stransfer
*qTsend
;
fzsend_data(qdaemon
, zdata
, cdata
, fendofmessage
)
if (iZlast_tx_data_packet
== -1 || iZlast_tx_data_packet
== ZCRCW
) {
if (!fzsend_hdr (qdaemon
, ZBIN
, ZDATA
,
hvzencode_data_hdr (wpZtxpos
), TRUE
))
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
;
iZlast_tx_data_packet
= ZCRCG
;
#if 0 /* <cZblklen_override> is currently unnecessary */
if (cZblklen_override
&& cZblklen
> cZblklen_override
)
cZblklen
= cZblklen_override
;
if (cZrx_buf_len
&& cZblklen
> cZrx_buf_len
)
if (FDEBUGGING(DEBUG_PROTO
)) {
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",
if (!fzsend_data_packet (qdaemon
, zdata
, n
, iZlast_tx_data_packet
,
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
)) {
|| ! ffileisopen (qTsend
->e
)) {
ulog (LOG_ERROR
, "Can't reset non-file");
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
));
* If we've reached the maximum transmit window size, let the
* 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
,
* 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
)) {
|| ! ffileisopen (qTsend
->e
)) {
ulog (LOG_ERROR
, "Can't reset non-file");
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
));
* Process existing data ...
while (fzreceive_ready ()) {
READCHAR (qdaemon
, ch
, 1);
/* 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)
c
= getinsync (qdaemon
, TRUE
);
/* 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
,
|| ! ffileisopen (qTsend
->e
)) {
"Attempt to back up non-file");
if (! ffileseek (qTsend
->e
,
wpZrxpos
- wpZtxstart
)) {
"seek: %s", strerror (errno
));
iZlast_tx_data_packet
= -1; /* trigger ZDATA */
break; /* not returning is intentional */
READCHAR (qdaemon
, ch
, XON_WAIT
);
* Wait for data to come in.
* This continues processing until a complete file or command has been
if (!fzsend_hdr (qdaemon
, ZHEX
, ZRPOS
,
hvzencode_data_hdr (wpZrxbytes
), FALSE
))
c
= izrecv_hdr (qdaemon
, rx_hdr
);
ulog (LOG_ERROR
, "fzwait: retries exhausted");
ulog (LOG_ERROR
, "fzwait: retries exhausted");
goto nxthdr
; /* ignore, partner is out of sync */
zdecode_data_hdr (rclhdr (rx_hdr
), &rx_bytes
);
DEBUG_MESSAGE2 (DEBUG_PROTO
,
"fzwait: bytes(us,them) 0x%lx,0x%lx",
if (rx_bytes
!= wpZrxbytes
) {
"fzwait: retries exhausted");
(void) zrdat32 (qdaemon
, zZrx_packet_buf
,
* 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.
continue; /* goto nxthdr? */
* 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,
if (FDEBUGGING(DEBUG_PROTO
)) {
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;
DEBUG_MESSAGE2 (DEBUG_PROTO
,
"fzwait: zrdat32: %s, %d bytes",
DEBUG_MESSAGE2 (DEBUG_PROTO
,
"fzwait: zrdat32: %d, %d bytes",
case ZM_ERROR
: /* CRC error */
"fzwait: retries exhausted");
"fzwait: retries exhausted");
iZlast_rx_data_packet
= ZCRCW
;
&& !fgot_data (qdaemon
, zZrx_packet_buf
,
if (!fzsend_hdr (qdaemon
, ZHEX
, ZACK
,
hvzencode_data_hdr (wpZrxbytes
),
if (! fsend_data (qdaemon
->qconn
, &xon
,
iZlast_rx_data_packet
= ZCRCQ
;
&& !fgot_data (qdaemon
, zZrx_packet_buf
,
if (!fzsend_hdr (qdaemon
, ZHEX
, ZACK
,
hvzencode_data_hdr (wpZrxbytes
),
iZlast_rx_data_packet
= ZCRCG
;
&& !fgot_data (qdaemon
, zZrx_packet_buf
,
iZlast_rx_data_packet
= ZCRCE
;
&& !fgot_data (qdaemon
, zZrx_packet_buf
,
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
if (!fzfinish_rx (qdaemon
))
if (!fgot_data (qdaemon
, zZrx_packet_buf
,
(long) -1, TRUE
, &fexit
))
* FIXME: Examine <fexit>?
* Or maybe ensure it's TRUE?
ulog (LOG_FATAL
, "fzwait: received header %s",
* 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)
fzfile(qdaemon
, qtrans
, fstart
, fsend
, cbytes
, pfhandled
)
struct stransfer
*qtrans
;
DEBUG_MESSAGE2 (DEBUG_PROTO
, "fzfile: fstart=%d, fsend=%d", fstart
,
if (! fzfinish_tx (qdaemon
, &iredo
))
if (! ffileisopen (qtrans
->e
)) {
"Attempt to back up non-file");
if (! ffileseek (qtrans
->e
, iredo
)) {
"seek: %s", strerror (errno
));
qtrans
->fsendfile
= TRUE
;
return fqueue_send (qdaemon
, qtrans
);
/****************************************************************************/
#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
* 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 */
/* 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 */
/* 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,
#define updcrc(cp, crc) (crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
(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
achdrval_t tx_hdr
,rx_hdr
;
for (i
= 0; i
< cZstartup_retries
; i
++) {
tx_hdr
[ZF0
] = ZPROTOCOL_VERSION
;
tx_hdr
[ZF1
] |= TX_ESCCTL
;
switch (izexchange_init (qdaemon
, ZINIT
, tx_hdr
, rx_hdr
)) {
#if 0 /* can't work, but kept for documentation */
ulog (LOG_ERROR
, "Old protocol version, init failed");
fZesc_ctl
= fZesc_ctl
|| (rx_hdr
[ZF1
] & TX_ESCCTL
) != 0;
switch (izexchange_init (qdaemon
, ZDATA
, tx_hdr
, rx_hdr
)) {
switch (izexchange_init (qdaemon
, ZACK
, tx_hdr
, rx_hdr
)) {
switch (izexchange_init (qdaemon
, ZINITEND
, tx_hdr
, rx_hdr
)) {
DEBUG_MESSAGE0 (DEBUG_PROTO
,
"fzstart_proto: Protocol started");
/* FIXME: see protg.c regarding sequencing here. */
ulog (LOG_ERROR
, "Protocol init failed");
* 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).
izexchange_init(qdaemon
, send_type
, send_val
, recv_val
)
for (i
= 0; i
< CEXCHANGE_INIT_RETRIES
; i
++) {
if (!fzsend_hdr (qdaemon
, send_type
== ZDATA
? ZBIN
: ZHEX
,
send_type
, rclhdr (send_val
), FALSE
))
* 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
, "",
if (!fsend_data (qdaemon
->qconn
, zZtx_packet_buf
,
recv_type
= izrecv_hdr (qdaemon
, recv_val
);
if (zrdat32 (qdaemon
, zZrx_packet_buf
, 1024, &count
)
if (recv_type
== send_type
)
* 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
)
* 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
)
* Shut down the protocol ...
fzshutdown_proto(qdaemon
)
(void) fzsend_hdr (qdaemon
, ZHEX
, ZFIN
, 0L, FALSE
);
* Reset the transmitter side for sending a new message ...
iZlast_tx_data_packet
= -1;
* <wpZlastsync> is set to -1L to suppress ZCRCW request otherwise
* triggered by (wpZlastsync == wpZtxpos).
wpZtxpos
= (wpZtxpos
+ 1024L) & ~1023L; /* next packet boundary */
wpZlrxpos
= wpZrxpos
= wpZtxpos
;
wpZtxstart
= wpZtxpos
; /* so we can compute the "file offset" */
* 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
* 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).
fzfinish_tx(qdaemon
, plredo
)
DEBUG_MESSAGE4 (DEBUG_PROTO
,
"fzfinish_tx: txpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, rxbytes=0x%lx",
wpZtxpos
, wpZrxpos
, wpZlrxpos
, wpZrxbytes
);
c
= izrecv_hdr (qdaemon
, rx_hdr
);
wpZrxpos
= lzupdate_rxpos (rx_hdr
, wpZrxpos
,
* 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
if (((wpZtxpos
+ 1024) & ~1023) == wpZrxpos
)
cZbytes_resent
+= wpZtxpos
- wpZrxpos
;
wpZlrxpos
= wpZtxpos
= wpZrxpos
;
if (wpZlastsync
== wpZrxpos
) {
/* FIXME: shouldn't we reset iZbeenhereb4? */
iZlast_tx_data_packet
= ZCRCW
; /* force a timeout */
*plredo
= wpZrxpos
- wpZtxstart
;
wpZrxpos
= lzupdate_rxpos (rx_hdr
, wpZrxpos
,
if (wpZtxpos
== wpZrxpos
) /* the ACK we want? */
* 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
);
break; /* ignore, out of sync (old) */
* 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
)
if (rx_bytes
== wpZrxbytes
) {
if (!fzsend_hdr (qdaemon
, ZHEX
, ZACK
,
hvzencode_data_hdr (wpZrxbytes
),
break; /* ignore, out of sync (old) */
"fzfinish_tx: retries exhausted");
* Normally the sender doesn't send NAK's for timeouts.
* We have to here because of the following scenario:
* - 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
),
"fzfinish_tx: retries exhausted");
if (!fzsend_hdr (qdaemon
, ZHEX
, ZNAK
,
hvzencode_data_hdr (wpZtxpos
),
* Initialize the receiver ...
wpZrxbytes
= (wpZrxbytes
+ 1024L) & ~1023L; /* next packet boundary */
* Terminate the receiver ...
* Acknowledge the last packet received.
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 ...
fzsend_hdr(qdaemon
, ipkttype
, ihdrtype
, hdrval
, fcheckreceive
)
DEBUG_MESSAGE2 (DEBUG_PROTO
, "fzsend_hdr: %s, data = 0x%lx",
ZZHEADER_NAME(ihdrtype
), hdrval
);
cpacketlen
= czbuild_header (zZtx_packet_buf
, ipkttype
,
if (ihdrtype
== ZACK
&& rand () % 100 < uucptest2
) {
if (ihdrtype
== ZACK
|| ihdrtype
== ZDATA
) {
extern int uucptest
,uucptest2
;
fresult
= fsend_data (qdaemon
->qconn
, zZtx_packet_buf
,
(size_t) cpacketlen
, fcheckreceive
);
return fsend_data (qdaemon
->qconn
, zZtx_packet_buf
,
(size_t) cpacketlen
, fcheckreceive
);
* Send a data packet to our partner ...
* <frameend> is one of ZCRCx.
fzsend_data_packet(qdaemon
, zdata
, cdata
, frameend
, fcheckreceive
)
cpacketlen
= czbuild_data_packet (zZtx_packet_buf
, zdata
, cdata
,
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().
czbuild_header(zresult
, ipkttype
, ihdrtype
, hdrval
)
p
= zputchar (p
, ihdrtype
);
crc
= UPDC32 (ihdrtype
, crc
);
stohdr (hdrval
, achdrval
);
for (i
= 0; i
< 4; i
++) {
p
= zputchar (p
, achdrval
[i
]);
crc
= UPDC32 (achdrval
[i
], crc
);
for (i
= 0; i
< 4; i
++) {
p
= zputchar (p
, (char) crc
);
case ZHEX
: /* build hex header */
p
= zputhex (p
, ihdrtype
);
crc
= UPDC32 (ihdrtype
, crc
);
stohdr (hdrval
, achdrval
);
for (i
= 0; i
< 4; i
++) {
p
= zputhex (p
, achdrval
[i
]);
crc
= UPDC32 (achdrval
[i
], crc
);
for (i
= 0; i
< 4; i
++) {
p
= zputhex (p
, (char) crc
);
* Uncork the remote in case a fake XOFF has stopped data flow.
if (ihdrtype
!= ZFIN
&& ihdrtype
!= ZACK
) /* FIXME: why? */
ulog (LOG_FATAL
, "czbuild_header: ipkttype == %d", ipkttype
);
* Build Zmodem data packets ...
* This function is zsdata() and zsda32() from the zm source.
czbuild_data_packet(zresult
, zdata
, cdata
, frameend
)
for ( ; cdata
-- != 0; zdata
++) {
crc
= UPDC32 ((unsigned char) c
, crc
);
crc
= UPDC32 (frameend
, crc
);
for (cdata
= 0; cdata
< 4; cdata
++) {
p
= zputchar (p
, (char) crc
);
if (frameend
== ZCRCW
|| frameend
== ZCRCE
|| frameend
== ZCRCF
) {
* This is function zgethdr() from the Zmodem source.
* 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) {
stohdr (hvZpkt_hdrval_kludge
, hdr
);
DEBUG_MESSAGE2 (DEBUG_PROTO
,
"izrecv_hdr: queued %s, data = 0x%lx",
ZZHEADER_NAME(c
), rclhdr (hdr
));
cerr
= cZmax_garbage
; /* Max bytes before start of frame */
switch (c
= noxrd7 (qdaemon
)) {
case ZPAD
: /* This is what we want */
case CR
: /* padding at end of previous header */
switch (c
= noxrd7 (qdaemon
)) {
case ZDLE
: /* This is what we want */
switch (c
= noxrd7 (qdaemon
)) {
c
= zrbhdr32 (qdaemon
, hdr
);
c
= zrhhdr (qdaemon
, hdr
);
DEBUG_MESSAGE2 (DEBUG_PROTO
, "izrecv_hdr: %s, data = 0x%x",
ZZHEADER_NAME(c
), rclhdr (hdr
));
* Receive a binary style header (type and position) with 32 bit FCS ...
if ((c
= zdlread (qdaemon
)) & ~0377)
for (i
= 0; i
< 4; i
++) {
if ((c
= zdlread (qdaemon
)) & ~0377)
for (i
= 0; i
< 4; i
++) {
if ((c
= zdlread (qdaemon
)) & ~0377)
* Receive a hex style header (type and position) ...
if ((c
= zgethex (qdaemon
)) < 0)
for (i
= 0; i
< 4; i
++) {
if ((c
= zgethex (qdaemon
)) < 0)
for (i
= 0; i
< 4; i
++) {
if ((c
= zgethex (qdaemon
)) < 0)
* Receive a data packet ...
zrdat32(qdaemon
, buf
, length
, iprxcount
)
if ((c
= zdlread (qdaemon
)) & ~0377) {
if ((c
= zdlread (qdaemon
)) & ~0377)
if ((c
= zdlread (qdaemon
)) & ~0377)
if ((c
= zdlread (qdaemon
)) & ~0377)
if ((c
= zdlread (qdaemon
)) & ~0377)
*iprxcount
= length
- (end
- buf
);
return ZM_ERROR
; /* bad packet, too long */
* Respond to receiver's complaint, get back in sync with receiver ...
c
= izrecv_hdr (qdaemon
, rx_hdr
);
wpZrxpos
= lzupdate_rxpos (rx_hdr
, wpZrxpos
,
cZbytes_resent
+= wpZtxpos
- wpZrxpos
;
wpZlrxpos
= wpZtxpos
= wpZrxpos
;
if (wpZlastsync
== wpZrxpos
) {
/* FIXME: shouldn't we reset iZbeenhereb4? */
wpZrxpos
= lzupdate_rxpos (rx_hdr
, wpZrxpos
,
if (flag
|| wpZtxpos
== wpZrxpos
)
* Our partner is in fzfinish_tx() and is waiting
zdecode_data_hdr (rclhdr (rx_hdr
), &rx_bytes
);
if (rx_bytes
== wpZrxbytes
) {
if (!fzsend_hdr (qdaemon
, ZHEX
, ZACK
,
hvzencode_data_hdr (wpZrxbytes
),
"getinsync: retries exhausted");
break; /* sender doesn't send ZNAK for timeout */
"getinsync: retries exhausted");
if (!fzsend_hdr (qdaemon
, ZHEX
, ZNAK
,
hvzencode_data_hdr (wpZtxpos
),
* Send a byte as two hex digits ...
static char digits
[] = "0123456789abcdef";
*p
++ = digits
[(ch
& 0xF0) >> 4];
* Send character c with ZMODEM escape sequence encoding ...
* FIXME: Escape CR following @ (Telenet net escape) ... disabled for now
* Will need to put back references to <lastsent>.
/* Quick check for non control characters */
if (!fZesc_ctl
&& (lastsent
& 0177) != '@')
if (fZesc_ctl
&& !(c
& 0140)) {
* Decode two lower case hex digits into an 8 bit byte value ...
if ((c
= noxrd7 (qdaemon
)) < 0)
if ((c
= noxrd7 (qdaemon
)) < 0)
* Read a byte, checking for ZMODEM escape encoding ...
READCHAR (qdaemon
, c
, cZtimeout
);
if (c
& 0140) /* quick check for non control characters */
READCHAR (qdaemon
, c
, XON_WAIT
);
if (fZesc_ctl
&& !(c
& 0140))
READCHAR (qdaemon
, c
, cZtimeout
);
case ZRUB0
: /* FIXME: This is never generated. */
case ZRUB1
: /* FIXME: This is never generated. */
READCHAR (qdaemon
, c
, XON_WAIT
);
if (fZesc_ctl
&& !(c
& 0140))
goto again2
; /* FIXME: why again2? */
* Read a character from the modem line with timeout ...
* Eat parity bit, XON and XOFF characters.
READCHAR (qdaemon
, c
, cZtimeout
);
READCHAR (qdaemon
, c
, XON_WAIT
);
if (fZesc_ctl
&& !(c
& 0140))
* 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?)
realreadchar(qdaemon
, timeout
)
if ((c
= breceive_char (qdaemon
->qconn
, timeout
, TRUE
)) >= 0)
ulog (LOG_FATAL
, "realreadchar: breceive_char() returned %d", c
);
* 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.
return iPrecstart
!= iPrecend
;
* Store integer value in an achdrval_t ...
hdr
[ZP1
] = (char) (val
>> 8);
hdr
[ZP2
] = (char) (val
>> 16);
hdr
[ZP3
] = (char) (val
>> 24);
* Recover an integer from a header ...
v
= (v
<< 8) | (hdr
[ZP2
] & 0377);
v
= (v
<< 8) | (hdr
[ZP1
] & 0377);
v
= (v
<< 8) | (hdr
[ZP0
] & 0377);
* 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.
hvzencode_data_hdr(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.
zdecode_data_hdr(hdrval
, pcbytes
)
* Update <wpZrxpos> from the received data header value ...
* FIXME: Here is where we'd handle wrapping around at 4 gigabytes.
lzupdate_rxpos(rx_hdr
, rxpos
, lrxpos
, txpos
)
winpos_t rxpos
,lrxpos
,txpos
;
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))