/* 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.1 1995/10/18 08:38:41 deraadt Exp $"; #endif #include #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: 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 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 */ static unsigned cZtxwcnt; /* counter used to space ack requests */ static unsigned cZrxwcnt; /* counter used to watch receiver's buf size */ static winpos_t wpZtxstart; /* 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 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 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 /* 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 ? * 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 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; /* * 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 ... * 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 . */ 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 ... * * 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 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 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 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 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 valid. It could be old. */ if (rx_pktpos < wpZlrxpos || rx_pktpos > ((wpZtxpos + 1024L) & ~1023L)) return rxpos; return rx_pktpos; }