AmendHub

Download:

jcs

/

subtext

/

amendments

/

182

zmodem: Add BSD-licensed ZMODEM implementation from TeraTerm

Lots of internal restructuring by me to keep all state within a
zmodem_session object with callbacks to network/modem functions to
actually do the data sending and receiving.

jcs made amendment 182 about 1 year ago
--- zmodem.c Mon Jul 4 23:44:50 2022 +++ zmodem.c Mon Jul 4 23:44:50 2022 @@ -0,0 +1,1534 @@ +/* + * Copyright (c) 2022 joshua stein <jcs@jcs.org> + * + * Copyright (C) 1994-1998 T. Teranishi + * (C) 2007- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdarg.h> +#if THINK_C +#include "util.h" +#else +#include <stdbool.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#if THINK_C +#include <unix.h> +#endif + +#include "zmodem.h" +#include "crc.h" + +#ifdef ZMODEM_DEBUG +#include "logger.h" +extern struct logger *logger; +#endif + +#define LOBYTE(w) ((unsigned char)(((unsigned short)(w)) & 0xff)) +#define HIBYTE(w) ((unsigned char)((((unsigned short)(w)) >> 8) & 0xff)) +#define LOWORD(l) ((unsigned short)(l)) +#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF)) + +#define Z_RecvInit 1 +#define Z_RecvInit2 2 +#define Z_RecvData 3 +#define Z_RecvFIN 4 +#define Z_SendInit 5 +#define Z_SendInitHdr 6 +#define Z_SendInitDat 7 +#define Z_SendFileHdr 8 +#define Z_SendFileDat 9 +#define Z_SendDataHdr 10 +#define Z_SendDataDat 11 +#define Z_SendDataDat2 12 +#define Z_SendEOF 13 +#define Z_SendFIN 14 +#define Z_SendSkip 15 +#define Z_Cancel 16 +#define Z_End 17 + +#define Z_PktGetPAD 1 +#define Z_PktGetDLE 2 +#define Z_PktHdrFrm 3 +#define Z_PktGetBin 4 +#define Z_PktGetHex 5 +#define Z_PktGetHexEOL 6 +#define Z_PktGetData 7 +#define Z_PktGetCRC 8 + +#define ZPAD '*' +#define ZDLE 0x18 +#define ZDLEE 0x58 +#define ZBIN 'A' +#define ZHEX 'B' +#define ZBIN32 'C' + +#define ZRQINIT 0 +#define ZRINIT 1 +#define ZSINIT 2 +#define ZACK 3 +#define ZFILE 4 +#define ZSKIP 5 +#define ZNAK 6 +#define ZABORT 7 +#define ZFIN 8 +#define ZRPOS 9 +#define ZDATA 10 +#define ZEOF 11 +#define ZFERR 12 +#define ZCRC 13 +#define ZCHALLENGE 14 +#define ZCOMPL 15 +#define ZCAN 16 +#define ZFREECNT 17 +#define ZCOMMAND 18 +#define ZSTDERR 19 + +#define ZCRCE 'h' +#define ZCRCG 'i' +#define ZCRCQ 'j' +#define ZCRCW 'k' +#define ZRUB0 'l' +#define ZRUB1 'm' + +#define ZF0 3 +#define ZF1 2 +#define ZF2 1 +#define ZF3 0 +#define ZP0 0 +#define ZP1 1 +#define ZP2 2 +#define ZP3 3 + +#define CANFDX 0x01 +#define CANOVIO 0x02 +#define CANBRK 0x04 +#define CANCRY 0x08 +#define CANLZW 0x10 +#define CANFC32 0x20 +#define ESCCTL 0x40 +#define ESC8 0x80 + +#define ZCBIN 1 +#define ZCNL 2 + +#define XON 0x11 + +short ZRead1Byte(struct zmodem_session *, unsigned char *); +void ZPutHex(struct zmodem_session *, short *, unsigned char); +void ZResetTimeout(struct zmodem_session *, short); +void ZShHdr(struct zmodem_session *, unsigned char); +void ZPutBin(struct zmodem_session *, short *, unsigned char); +void ZSbHdr(struct zmodem_session *, unsigned char); +void ZStoHdr(struct zmodem_session *, unsigned long); +long ZRclHdr(struct zmodem_session *); +void ZSendRInit(struct zmodem_session *); +void ZSendRQInit(struct zmodem_session *); +void ZSendRPOS(struct zmodem_session *); +void ZSendACK(struct zmodem_session *); +void ZSendNAK(struct zmodem_session *); +void ZSendEOF(struct zmodem_session *); +void ZSendFIN(struct zmodem_session *); +void ZSendSkip(struct zmodem_session *); +void ZSendCancel(struct zmodem_session *); +void ZSendInitHdr(struct zmodem_session *); +void ZSendInitDat(struct zmodem_session *); +void ZSendFileHdr(struct zmodem_session *); +void ZSendFileDat(struct zmodem_session *); +void ZSendDataHdr(struct zmodem_session *); +void ZSendDataDat(struct zmodem_session *); +bool ZCheckHdr(struct zmodem_session *); +void ZParseRInit(struct zmodem_session *); +bool ZParseSInit(struct zmodem_session *); +void ZParseHdr(struct zmodem_session *); +bool FTCreateFile(struct zmodem_session *); +bool ZParseFile(struct zmodem_session *); +bool ZWriteData(struct zmodem_session *); +void ZCheckData(struct zmodem_session *); + +#ifdef ZMODEM_DEBUG + +#define LOGBUFSIZE 256 +static char recvbuf[LOGBUFSIZE]; +static char sendbuf[LOGBUFSIZE]; + +static const char * +hdrtype_name(short type) +{ + static const char *s[] = { + "ZRQINIT", + "ZRINIT", + "ZSINIT", + "ZACK", + "ZFILE", + "ZSKIP", + "ZNAK", + "ZABORT", + "ZFIN", + "ZRPOS", + "ZDATA", + "ZEOF", + "ZFERR", + "ZCRC", + "ZCHALLENGE", + "ZCOMPL", + "ZCAN", + "ZFREECNT", + "ZCOMMAND", + "ZSTDERR", + }; + + if (type >= ZRQINIT && type <= ZSTDERR) + return (s[type]); + else + return NULL; +} + +static const char * +state_name(struct zmodem_session *zs) +{ + static const char *s[] = { + "", + "Z_RecvInit", + "Z_RecvInit2", + "Z_RecvData", + "Z_RecvFIN", + "Z_SendInit", + "Z_SendInitHdr", + "Z_SendInitDat", + "Z_SendFileHdr", + "Z_SendFileDat", + "Z_SendDataHdr", + "Z_SendDataDat", + "Z_SendDataDat2", + "Z_SendEOF", + "Z_SendFIN", + "Z_SendSkip", + "Z_Cancel", + "Z_End", + }; + + if (zs->ZState >= Z_RecvInit && zs->ZState <= Z_End) + return (s[zs->ZState]); + else + return NULL; +} + +static void +add_recvbuf(struct zmodem_session *zs, char *fmt, ...) +{ + va_list arg; + char buf[128]; + + va_start(arg, fmt); + vsnprintf(buf, sizeof(buf), fmt, arg); + strlcat(recvbuf, buf, sizeof(recvbuf)); + va_end(arg); + + logger_printf(logger, "recvbuf: %s (state %s)", recvbuf, state_name(zs)); + recvbuf[0] = '\0'; +} + +static void +add_sendbuf(struct zmodem_session *zs, char *fmt, ...) +{ + va_list arg; + char buf[128]; + + va_start(arg, fmt); + vsnprintf(buf, sizeof(buf), fmt, arg); + strlcat(sendbuf, buf, sizeof(sendbuf)); + va_end(arg); + + logger_printf(logger, "recvbuf: %s (state %s)", recvbuf, state_name(zs)); + sendbuf[0] = '\0'; +} + +#endif /* ZMODEM_DEBUG */ + +struct zmodem_session * +ZCreateSender(FILE *fp) +{ + struct zmodem_session *zs; + + if (!fp) + return NULL; + + zs = malloc(sizeof(struct zmodem_session)); + if (zs == NULL) { + return NULL; + } + memset(zs, 0, sizeof(*zs)); + + zs->file = fp; + fseek(zs->file, 0, SEEK_END); + zs->file_size = ftell(zs->file); + zs->BinFlag = true; + + zs->ZMode = IdZAutoS; + + return zs; +} + +struct zmodem_session * +ZCreateReceiver(void) +{ + struct zmodem_session *zs; + + zs = malloc(sizeof(struct zmodem_session)); + if (zs == NULL) { + return NULL; + } + memset(zs, 0, sizeof(*zs)); + + zs->ZMode = IdZAutoR; + + return zs; +} + +short +ZRead1Byte(struct zmodem_session *zs, unsigned char *b) +{ + if (zs->recv(zs->cookie, b) == 0) + return 0; + + /* ignore 0x11, 0x13, 0x81 and 0x83 */ + if (((*b & 0x7F) == 0x11) || ((*b & 0x7F) == 0x13)) + return 0; + + return 1; +} + +void +ZPutHex(struct zmodem_session *zs, short *i, unsigned char b) +{ + if (b <= 0x9f) + zs->PktOut[*i] = (b >> 4) + 0x30; + else + zs->PktOut[*i] = (b >> 4) + 0x57; + + (*i)++; + + if ((b & 0x0F) <= 0x09) + zs->PktOut[*i] = (b & 0x0F) + 0x30; + else + zs->PktOut[*i] = (b & 0x0F) + 0x57; + + (*i)++; +} + +void +ZResetTimeout(struct zmodem_session *zs, short seconds) +{ + zs->TimeOutAt = time(NULL) + seconds; +} + +bool +ZHaveTimedOut(struct zmodem_session *zs) +{ + if (zs->TimeOutAt && time(NULL) >= zs->TimeOutAt) + return true; + + return false; +} + +void +ZShHdr(struct zmodem_session *zs, unsigned char HdrType) +{ + short i; + + zs->PktOut[0] = ZPAD; + zs->PktOut[1] = ZPAD; + zs->PktOut[2] = ZDLE; + zs->PktOut[3] = ZHEX; + zs->PktOutCount = 4; + ZPutHex(zs, &(zs->PktOutCount), HdrType); + zs->CRC = UpdateCRC(HdrType, 0); + for (i = 0; i <= 3; i++) { + ZPutHex(zs, &(zs->PktOutCount), zs->TxHdr[i]); + zs->CRC = UpdateCRC(zs->TxHdr[i], zs->CRC); + } + ZPutHex(zs, &(zs->PktOutCount), HIBYTE(zs->CRC)); + ZPutHex(zs, &(zs->PktOutCount), LOBYTE(zs->CRC)); + zs->PktOut[zs->PktOutCount] = '\r'; //0x8D; + zs->PktOutCount++; + zs->PktOut[zs->PktOutCount] = '\n'; //0x8A; + zs->PktOutCount++; + + if ((HdrType != ZFIN) && (HdrType != ZACK)) { + zs->PktOut[zs->PktOutCount] = XON; + zs->PktOutCount++; + } + + zs->PktOutPtr = 0; + zs->Sending = true; + +#ifdef ZMODEM_DEBUG + add_sendbuf(zs, "ZShHdr: %s", hdrtype_name(HdrType)); + + if (HdrType == ZRPOS) { + long pos; + memcpy(&pos, (unsigned long *)&zs->TxHdr[ZP0], 4); + add_sendbuf(zs, " offset=%ld", pos); + } +#endif +} + +/* + * In lrzsz, ZDLE (CAN), DLE, XON, XOFF, CR immediately after @, and these + * Characters with MSB are escaped. + * It seems that it was the same as lrzsz in Tera Term before, but for some + * reason CR is always changed to escape target. + * + * To avoid problems if you connect to ssh / telnet from the destination + * LF and GS are also added to the default escape target. + * ssh: ~ immediately after LF or CR is treated as an escape character + * telnet: GS is the escape character + */ +void +ZPutBin(struct zmodem_session *zs, short *i, unsigned char b) +{ + switch (b) { + case 0x0D: // CR + case 0x8D: // CR | 0x80 + // if (zs->CtlEsc || ((zs->LastSent & 0x7f) == '@')) { + zs->PktOut[*i] = ZDLE; + (*i)++; + b = b ^ 0x40; + // } + break; + case 0x0A: // LF + case 0x10: // DLE + case 0x11: // XON + case 0x13: // XOFF + case 0x1d: // GS + case ZDLE: // CAN(0x18) + case 0x8A: // LF | 0x80 + case 0x90: // DLE | 0x80 + case 0x91: // XON | 0x80 + case 0x93: // XOFF | 0x80 + case 0x9d: // GS | 0x80 + zs->PktOut[*i] = ZDLE; + (*i)++; + b = b ^ 0x40; + break; + default: + if (zs->CtlEsc && ((b & 0x60) == 0)) { + zs->PktOut[*i] = ZDLE; + (*i)++; + b = b ^ 0x40; + } + } + zs->LastSent = b; + zs->PktOut[*i] = b; + (*i)++; +} + +void +ZSbHdr(struct zmodem_session *zs, unsigned char HdrType) +{ + short i; + + zs->PktOut[0] = ZPAD; + zs->PktOut[1] = ZDLE; + zs->PktOut[2] = ZBIN; + zs->PktOutCount = 3; + ZPutBin(zs, &(zs->PktOutCount), HdrType); + zs->CRC = UpdateCRC(HdrType, 0); + for (i = 0; i <= 3; i++) { + ZPutBin(zs, &(zs->PktOutCount), zs->TxHdr[i]); + zs->CRC = UpdateCRC(zs->TxHdr[i], zs->CRC); + } + ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC)); + ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC)); + + zs->PktOutPtr = 0; + zs->Sending = true; + +#ifdef ZMODEM_DEBUG + add_sendbuf(zs, "ZSbHdr: %s ", hdrtype_name(HdrType)); +#endif +} + +void +ZStoHdr(struct zmodem_session *zs, unsigned long Pos) +{ + zs->TxHdr[ZP0] = LOBYTE(LOWORD(Pos)); + zs->TxHdr[ZP1] = HIBYTE(LOWORD(Pos)); + zs->TxHdr[ZP2] = LOBYTE(HIWORD(Pos)); + zs->TxHdr[ZP3] = HIBYTE(HIWORD(Pos)); +} + +long +ZRclHdr(struct zmodem_session *zs) +{ + unsigned long L; + + L = (unsigned char)(zs->RxHdr[ZP3]); + L = (L << 8) + (unsigned char)(zs->RxHdr[ZP2]); + L = (L << 8) + (unsigned char)(zs->RxHdr[ZP1]); + return ((L << 8) + (unsigned char)(zs->RxHdr[ZP0])); +} + +void +ZSendRInit(struct zmodem_session *zs) +{ + zs->Pos = 0; + ZStoHdr(zs, 0); + zs->TxHdr[ZF0] = /* CANFC32 | */ CANFDX | CANOVIO; + if (zs->CtlEsc) + zs->TxHdr[ZF0] = zs->TxHdr[ZF0] | ESCCTL; + ZShHdr(zs, ZRINIT); +} + +void +ZSendRQInit(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + ZShHdr(zs, ZRQINIT); +} + +void +ZSendRPOS(struct zmodem_session *zs) +{ + ZStoHdr(zs, zs->Pos); + ZShHdr(zs, ZRPOS); + ZResetTimeout(zs, zs->TimeOut); +} + +void +ZSendACK(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + ZShHdr(zs, ZACK); + ZResetTimeout(zs, zs->TimeOut); +} + +void +ZSendNAK(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + ZShHdr(zs, ZNAK); +} + +void +ZSendEOF(struct zmodem_session *zs) +{ + ZStoHdr(zs, zs->Pos); + ZShHdr(zs, ZEOF); + zs->ZState = Z_SendEOF; +} + +void +ZSendFIN(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + ZShHdr(zs, ZFIN); +} + +void +ZSendSkip(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + ZShHdr(zs, ZSKIP); +} + +void +ZSendCancel(struct zmodem_session *zs) +{ + short i; + + for (i = 0; i <= 7; i++) + zs->PktOut[i] = ZDLE; + for (i = 8; i <= 17; i++) + zs->PktOut[i] = 0x08; + zs->PktOutCount = 18; + zs->PktOutPtr = 0; + zs->Sending = true; + zs->ZState = Z_Cancel; + +#ifdef ZMODEM_DEBUG + add_sendbuf(zs, "ZSendCancel"); +#endif +} + +void +ZSendInitHdr(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + if (zs->CtlEsc) + zs->TxHdr[ZF0] = ESCCTL; + ZShHdr(zs, ZSINIT); + zs->ZState = Z_SendInitHdr; +} + +void +ZSendInitDat(struct zmodem_session *zs) +{ + zs->CRC = 0; + zs->PktOutCount = 0; + ZPutBin(zs, &(zs->PktOutCount), 0); + zs->CRC = UpdateCRC(0, zs->CRC); + + zs->PktOut[zs->PktOutCount] = ZDLE; + zs->PktOutCount++; + zs->PktOut[zs->PktOutCount] = ZCRCW; + zs->PktOutCount++; + zs->CRC = UpdateCRC(ZCRCW, zs->CRC); + + ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC)); + ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC)); + + zs->PktOutPtr = 0; + zs->Sending = true; + zs->ZState = Z_SendInitDat; + +#ifdef ZMODEM_DEBUG + add_sendbuf(zs, "ZSendInitDat"); +#endif +} + +void +ZSendFileHdr(struct zmodem_session *zs) +{ + ZStoHdr(zs, 0); + if (zs->BinFlag) + zs->TxHdr[ZF0] = ZCBIN; /* binary file */ + else + zs->TxHdr[ZF0] = ZCNL; /* text file, convert newline */ + ZSbHdr(zs, ZFILE); + zs->ZState = Z_SendFileHdr; +} + +void +ZSendFileDat(struct zmodem_session *zs) +{ + short i, j; + struct stat sb; + + if (!zs->file) { + ZSendCancel(zs); + return; + } + + /* file name */ + strlcpy((char *)zs->PktOut, zs->file_name, sizeof(zs->PktOut)); + zs->PktOutCount = strlen((char *)zs->PktOut); + zs->CRC = 0; + for (i = 0; i <= zs->PktOutCount - 1; i++) + zs->CRC = UpdateCRC(zs->PktOut[i], zs->CRC); + ZPutBin(zs, &(zs->PktOutCount), 0); + zs->CRC = UpdateCRC(0, zs->CRC); + + /* + * ZFILE fields: + * size (dec) + * mtime (oct) + * mode (oct), + * serial (oct) + * files remaining (dec) + * bytes remaining (dec) + */ +#if THINK_C + snprintf((char *)zs->PktOut + zs->PktOutCount, + sizeof(zs->PktOut) - zs->PktOutCount, + "%lu 0 100644 0 0", zs->file_size); +#else + fstat(fileno(zs->file), &sb); + snprintf(&(zs->PktOut[zs->PktOutCount]), + sizeof(zs->PktOut) - zs->PktOutCount, + "%lu %lo %o", zs->file_size, (unsigned long)sb.st_mtim.tv_sec, + sb.st_mode); +#endif + j = strlen((char *)zs->PktOut + zs->PktOutCount); + for (i = 0; i <= j; i++) { + zs->CRC = UpdateCRC(zs->PktOut[zs->PktOutCount], zs->CRC); + zs->PktOutCount++; + } + + ZPutBin(zs, &(zs->PktOutCount), 0); + zs->CRC = UpdateCRC(0, zs->CRC); + zs->PktOut[zs->PktOutCount] = ZDLE; + zs->PktOutCount++; + zs->PktOut[zs->PktOutCount] = ZCRCW; + zs->PktOutCount++; + zs->CRC = UpdateCRC(ZCRCW, zs->CRC); + + ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC)); + ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC)); + + zs->PktOutPtr = 0; + zs->Sending = true; + zs->ZState = Z_SendFileDat; + + fseek(zs->file, 0, SEEK_SET); + +#ifdef ZMODEM_DEBUG + add_sendbuf(zs, "ZSendFileDat: ZFILE: ZF0=%x ZF1=%x ZF2=%x file=%s size=%lu", + zs->TxHdr[ZF0], zs->TxHdr[ZF1],zs->TxHdr[ZF2], + zs->file_name, zs->file_size); +#endif +} + +void +ZSendDataHdr(struct zmodem_session *zs) +{ + ZStoHdr(zs, zs->Pos); + ZSbHdr(zs, ZDATA); + zs->ZState = Z_SendDataHdr; +} + +void +ZSendDataDat(struct zmodem_session *zs) +{ + short c; + unsigned char b; + + if (zs->Pos >= zs->file_size) { + zs->Pos = zs->file_size; + ZSendEOF(zs); + fclose(zs->file); + zs->file = NULL; + return; + } + + if (zs->Pos != ftell(zs->file)) + fseek(zs->file, zs->Pos, SEEK_SET); + + zs->CRC = 0; + zs->PktOutCount = 0; + do { + c = fread(&b, 1, 1, zs->file); + if (c > 0) { + ZPutBin(zs, &(zs->PktOutCount), b); + zs->CRC = UpdateCRC(b, zs->CRC); + } + } while ((c != 0) && (zs->PktOutCount <= zs->MaxDataLen - 2)); + + zs->Pos = ftell(zs->file); + + zs->PktOut[zs->PktOutCount] = ZDLE; + zs->PktOutCount++; + if (zs->Pos >= zs->file_size) + b = ZCRCE; + else if ((zs->WinSize >= 0) && (zs->Pos - zs->LastPos > zs->WinSize)) + b = ZCRCQ; + else + b = ZCRCG; + zs->PktOut[zs->PktOutCount] = b; + zs->PktOutCount++; + zs->CRC = UpdateCRC(b, zs->CRC); + + ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC)); + ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC)); + + zs->PktOutPtr = 0; + zs->Sending = true; + if (b == ZCRCQ) + zs->ZState = Z_SendDataDat2; /* wait for response from receiver */ + else + zs->ZState = Z_SendDataDat; + +#ifdef ZMODEM_DEBUG + /* add_sendbuf("ZSendDataDat pos=%ld", zs->Pos); */ +#endif +} + +bool +ZInit(struct zmodem_session *zs) +{ + zs->CtlEsc = 0; + zs->MaxDataLen = 1024; + zs->WinSize = 32767; + + if (zs->ZMode == IdZAutoR || zs->ZMode == IdZAutoS) { + if (zs->ZMode == IdZAutoR) { + zs->ZMode = IdZReceive; + zs->send(zs->cookie, (unsigned char *)'0', 1); + } else { + zs->ZMode = IdZSend; + zs->send(zs->cookie, (unsigned char *)'1', 1); + } + zs->send(zs->cookie, (unsigned char *)'0', 1); + zs->send(zs->cookie, (unsigned char *)'B', 1); + zs->send(zs->cookie, (unsigned char *)ZDLE, 1); + zs->send(zs->cookie, (unsigned char *)ZPAD, 1); + } + + zs->PktOutCount = 0; + zs->Pos = 0; + zs->LastPos = 0; + zs->ZPktState = Z_PktGetPAD; + zs->Sending = false; + zs->LastSent = 0; + zs->CanCount = 5; + + if (zs->MaxDataLen <= 0) + zs->MaxDataLen = 1024; + if (zs->MaxDataLen < 64) + zs->MaxDataLen = 64; + if (zs->MaxDataLen > 1024) + zs->MaxDataLen = 1024; + + zs->TimeOut = 10; + ZResetTimeout(zs, zs->TimeOut); + + switch (zs->ZMode) { + case IdZReceive: + zs->ZState = Z_RecvInit; + ZSendRInit(zs); + break; + case IdZSend: + zs->ZState = Z_SendInit; +#if 0 + zs->output("rz\r", 3); +#endif + ZSendRQInit(zs); + break; + } + + return true; +} + +void +ZTimeOutProc(struct zmodem_session *zs) +{ + switch (zs->ZState) { + case Z_RecvInit: + ZSendRInit(zs); + break; + case Z_RecvInit2: + ZSendACK(zs); /* Ack for ZSINIT */ + break; + case Z_RecvData: + ZSendRPOS(zs); + break; + case Z_RecvFIN: + zs->ZState = Z_End; + break; + } +} + +bool +ZCheckHdr(struct zmodem_session *zs) +{ + short i; + bool ok; + + if (zs->CRC32) { + zs->CRC3 = 0xFFFFFFFF; + for (i = 0; i <= 8; i++) + zs->CRC3 = UpdateCRC32(zs->PktIn[i], zs->CRC3); + ok = (zs->CRC3 == 0xDEBB20E3); + } else { + zs->CRC = 0; + for (i = 0; i <= 6; i++) + zs->CRC = UpdateCRC(zs->PktIn[i], zs->CRC); + ok = (zs->CRC == 0); + } + + if (!ok) { + switch (zs->ZState) { + case Z_RecvInit: + ZSendRInit(zs); + break; + case Z_RecvData: + ZSendRPOS(zs); + break; + } + } + zs->RxType = zs->PktIn[0]; + for (i = 1; i <= 4; i++) + zs->RxHdr[i - 1] = zs->PktIn[i]; + + return ok; +} + +void +ZParseRInit(struct zmodem_session *zs) +{ + short max; + + if ((zs->ZState != Z_SendInit) && (zs->ZState != Z_SendEOF)) + return; + + if (!zs->file) { + zs->ZState = Z_SendFIN; + ZSendFIN(zs); + return; + } + + if (zs->CtlEsc) { + if ((zs->RxHdr[ZF0] & ESCCTL) == 0) { + zs->ZState = Z_SendInitHdr; + ZSendInitHdr(zs); + return; + } + } else + zs->CtlEsc = (zs->RxHdr[ZF0] & ESCCTL) != 0; + + max = (zs->RxHdr[ZP1] << 8) + zs->RxHdr[ZP0]; + if (max <= 0) + max = 1024; + if (zs->MaxDataLen > max) + zs->MaxDataLen = max; + + zs->ZState = Z_SendFileHdr; + ZSendFileHdr(zs); +} + +bool +ZParseSInit(struct zmodem_session *zs) +{ + if (zs->ZState != Z_RecvInit) + return false; + + zs->ZState = Z_RecvInit2; + zs->CtlEsc = zs->CtlEsc || ((zs->RxHdr[ZF0] & ESCCTL) != 0); + + return true; +} + +void +ZParseHdr(struct zmodem_session *zs) +{ +#ifdef ZMODEM_DEBUG + add_recvbuf(zs, "ZParseHdr: RxType %s ", hdrtype_name(zs->RxType)); +#endif + + switch (zs->RxType) { + case ZRQINIT: + if (zs->ZState == Z_RecvInit) + ZSendRInit(zs); + break; + case ZRINIT: + ZParseRInit(zs); + break; + case ZSINIT: + zs->ZPktState = Z_PktGetData; + if (zs->ZState == Z_RecvInit) + ZResetTimeout(zs, zs->TimeOut); + break; + case ZACK: + switch (zs->ZState) { + case Z_SendInitDat: + ZSendFileHdr(zs); + break; + case Z_SendDataDat2: + zs->LastPos = ZRclHdr(zs); + ZResetTimeout(zs, zs->TimeOut); + if (zs->Pos == zs->LastPos) + ZSendDataDat(zs); + else { + zs->Pos = zs->LastPos; + ZSendDataHdr(zs); + } + break; + } + break; + case ZFILE: + zs->ZPktState = Z_PktGetData; + if ((zs->ZState == Z_RecvInit) || (zs->ZState == Z_RecvInit2)) { + zs->BinFlag = (zs->RxHdr[ZF0] != ZCNL); + ZResetTimeout(zs, zs->TimeOut); + } + break; + case ZSKIP: + if (zs->file) { + fclose(zs->file); + /* + * If you try to send a file that exists on the server + * side, ZParseRInit () will double close, so + * Drop the flag here. (2007.12.20 yutaka) + */ + zs->file = NULL; + } + ZStoHdr(zs, 0); + if (zs->CtlEsc) + zs->RxHdr[ZF0] = ESCCTL; + zs->ZState = Z_SendInit; + ZParseRInit(zs); + break; + case ZNAK: + switch (zs->ZState) { + case Z_SendInitHdr: + case Z_SendInitDat: + ZSendInitHdr(zs); + break; + case Z_SendFileHdr: + case Z_SendFileDat: + ZSendFileHdr(zs); + break; + } + break; + case ZABORT: + case ZFERR: + if (zs->ZMode == IdZSend) { + zs->ZState = Z_SendFIN; + ZSendFIN(zs); + } + break; + case ZFIN: + if (zs->ZMode == IdZReceive) { + zs->ZState = Z_RecvFIN; + ZSendFIN(zs); + zs->CanCount = 2; + ZResetTimeout(zs, zs->TimeOut); + } else { + zs->ZState = Z_End; + zs->send(zs->cookie, (unsigned char *)"OO", 2); + } + break; + case ZRPOS: + switch (zs->ZState) { + case Z_SendFileDat: + case Z_SendDataHdr: + case Z_SendDataDat: + case Z_SendDataDat2: + case Z_SendEOF: + zs->Pos = ZRclHdr(zs); + zs->LastPos = zs->Pos; +#ifdef ZMODEM_DEBUG + add_recvbuf(zs, " pos=%ld", zs->Pos); +#endif + ZSendDataHdr(zs); + break; + } + break; + case ZDATA: + if (zs->Pos != ZRclHdr(zs)) { + ZSendRPOS(zs); + return; + } else { + ZResetTimeout(zs, zs->TimeOut); + zs->ZPktState = Z_PktGetData; + } + break; + case ZEOF: + if (zs->Pos != ZRclHdr(zs)) { + ZSendRPOS(zs); + return; + } + + if (zs->file) { + if (zs->CRRecv) { + zs->CRRecv = false; + fwrite("\012", 1, 1, zs->file); + } + fclose(zs->file); + zs->file = NULL; + } + zs->ZState = Z_RecvInit; + ZSendRInit(zs); + break; + } + zs->Quoted = false; + zs->CRC = 0; + zs->CRC3 = 0xFFFFFFFF; + zs->PktInPtr = 0; + zs->PktInCount = 0; +} + +bool +FTCreateFile(struct zmodem_session *zs) +{ + /* check for existing file, bail if it exists */ + zs->file = fopen(zs->file_name, "r"); + if (zs->file != NULL) { + fclose(zs->file); + zs->file = NULL; + return false; + } + + zs->file = fopen(zs->file_name, "wb+"); + if (zs->file == NULL) + return false; + + return true; +} + +bool +ZParseFile(struct zmodem_session *zs) +{ + unsigned char b; + short i; + char *p, *tfile_name; + unsigned long modtime, file_size; + short mode; + short ret; + + if ((zs->ZState != Z_RecvInit) && (zs->ZState != Z_RecvInit2)) + return false; + + /* kill timer */ + zs->TimeOutAt = 0; + zs->CRRecv = false; + + /* file name */ + zs->PktIn[zs->PktInPtr] = '\0'; + + /* don't allow paths in file name */ + tfile_name = (char *)&zs->PktIn; + i = 0; + do { + if (tfile_name[i] == '\0') + break; + if (tfile_name[i] == '/') { + tfile_name += i + 1; + i = 0; + } + i++; + } while (1); + + zs->file_name = xstrdup(tfile_name); + if (zs->file_name == NULL) + return false; + if (zs->file_name[0] == '\0') { + free(zs->file_name); + return false; + } + + /* file open */ + if (!FTCreateFile(zs)) { + free(zs->file_name); + zs->file_name = NULL; + return false; + } + + /* file size */ + i = strlen((char *)zs->PktIn) + 1; + file_size = 0; + do { + b = zs->PktIn[i]; + if ((b >= 0x30) && (b <= 0x39)) + file_size = (file_size * 10) + b - 0x30; + i++; + } while ((b >= 0x30) && (b <= 0x39)); + zs->file_size = file_size; + + /* file mtime */ + p = (char *)zs->PktIn + i; + zs->file_mtime = 0; + if (*p) { + ret = sscanf(p, "%lo%o", &modtime, &mode); + if (ret >= 1) + zs->file_mtime = modtime; + } + + zs->Pos = 0; + fseek(zs->file, 0, SEEK_SET); + + ZStoHdr(zs, 0); + zs->ZState = Z_RecvData; + + /* set timeout for data */ + ZResetTimeout(zs, zs->TimeOut); + + return true; +} + +bool +ZWriteData(struct zmodem_session *zs) +{ + short i; + unsigned char b; + + if (zs->ZState != Z_RecvData) + return false; + + /* kill timer */ + zs->TimeOutAt = 0; + + if (zs->BinFlag) + fwrite(zs->PktIn, zs->PktInPtr, 1, zs->file); + else + for (i = 0; i <= zs->PktInPtr - 1; i++) { + b = zs->PktIn[i]; + if ((b == 0x0A) && (!zs->CRRecv)) + fwrite("\015", 1, 1, zs->file); + if (zs->CRRecv && (b != 0x0A)) + fwrite("\012", 1, 1, zs->file); + zs->CRRecv = b == 0x0D; + fwrite(&b, 1, 1, zs->file); + } + + zs->Pos += zs->PktInPtr; + fseek(zs->file, zs->Pos, SEEK_SET); + ZStoHdr(zs, zs->Pos); + + /* set timeout for data */ + ZResetTimeout(zs, zs->TimeOut); + + return true; +} + +void +ZCheckData(struct zmodem_session *zs) +{ + bool ok; + + /* check CRC */ + if ((zs->CRC32 && zs->CRC3 != 0xDEBB20E3) || + (!zs->CRC32 && zs->CRC != 0)) { + switch (zs->ZState) { + case Z_RecvInit: + case Z_RecvInit2: + ZSendNAK(zs); + break; + case Z_RecvData: + ZSendRPOS(zs); + break; + } + zs->ZPktState = Z_PktGetPAD; + return; + } + /* parse data */ + switch (zs->RxType) { + case ZSINIT: + ok = ZParseSInit(zs); + break; + case ZFILE: + ok = ZParseFile(zs); + break; + case ZDATA: + ok = ZWriteData(zs); + break; + default: + ok = false; + } + + if (!ok) { + zs->ZPktState = Z_PktGetPAD; + zs->ZState = Z_SendSkip; + ZSendSkip(zs); + return; + } + + if (zs->RxType == ZFILE) + ZShHdr(zs, ZRPOS); + + /* next state */ + switch (zs->TERM) { + case ZCRCE: + zs->ZPktState = Z_PktGetPAD; + break; + case ZCRCG: + zs->ZPktState = Z_PktGetData; + break; + case ZCRCQ: + zs->ZPktState = Z_PktGetData; + if (zs->RxType != ZFILE) + ZShHdr(zs, ZACK); + break; + case ZCRCW: + zs->ZPktState = Z_PktGetPAD; + if (zs->RxType != ZFILE) + ZShHdr(zs, ZACK); + break; + default: + zs->ZPktState = Z_PktGetPAD; + } + + if (zs->ZPktState == Z_PktGetData) { + zs->Quoted = false; + zs->CRC = 0; + zs->CRC3 = 0xFFFFFFFF; + zs->PktInPtr = 0; + zs->PktInCount = 0; + } +} + +bool +ZParse(struct zmodem_session *zs) +{ + unsigned char b; + short c; + + do { + /* Send packet */ + if (zs->Sending) { + c = 1; + while ((c > 0) && (zs->PktOutCount > 0)) { + c = zs->send(zs->cookie, &(zs->PktOut[zs->PktOutPtr]), + zs->PktOutCount); + zs->PktOutPtr = zs->PktOutPtr + c; + zs->PktOutCount = zs->PktOutCount - c; + } + if (zs->PktOutCount <= 0) + zs->Sending = false; + if ((zs->ZMode == IdZReceive) && (zs->PktOutCount > 0)) + return true; + } + + c = ZRead1Byte(zs, &b); + while (c > 0) { + if (zs->ZState == Z_RecvFIN) { + if (b == 'O') + zs->CanCount--; + if (zs->CanCount <= 0) { + zs->ZState = Z_End; + return false; + } + } else { + switch (b) { + case ZDLE: + zs->CanCount--; + if (zs->CanCount <= 0) { + zs->ZState = Z_End; + return false; + } + break; + default: + zs->CanCount = 5; + } + } + + switch (zs->ZPktState) { + case Z_PktGetPAD: + switch (b) { + case ZPAD: + zs->ZPktState = Z_PktGetDLE; + break; + } + break; + case Z_PktGetDLE: + switch (b) { + case ZPAD: + break; + case ZDLE: + zs->ZPktState = Z_PktHdrFrm; + break; + default: + zs->ZPktState = Z_PktGetPAD; + } + break; + case Z_PktHdrFrm: /* Get header format type */ + switch (b) { + case ZBIN: + zs->CRC32 = false; + zs->PktInCount = 7; + zs->ZPktState = Z_PktGetBin; + break; + case ZHEX: + zs->HexLo = false; + zs->CRC32 = false; + zs->PktInCount = 7; + zs->ZPktState = Z_PktGetHex; + break; + case ZBIN32: + zs->CRC32 = true; + zs->PktInCount = 9; + zs->ZPktState = Z_PktGetBin; + break; + default: + zs->ZPktState = Z_PktGetPAD; + } + zs->Quoted = false; + zs->PktInPtr = 0; + break; + case Z_PktGetBin: + switch (b) { + case ZDLE: + zs->Quoted = true; + break; + default: + if (zs->Quoted) { + switch (b) { + case ZRUB0: + b = 0x7f; + break; + case ZRUB1: + b = 0xff; + break; + default: + b = b ^ 0x40; + } + zs->Quoted = false; + } + zs->PktIn[zs->PktInPtr] = b; + zs->PktInPtr++; + zs->PktInCount--; + if (zs->PktInCount == 0) { + zs->ZPktState = Z_PktGetPAD; + if (ZCheckHdr(zs)) + ZParseHdr(zs); + } + } + break; + case Z_PktGetHex: + if (b <= '9') + b = b - 0x30; + else if ((b >= 'a') && (b <= 'f')) + b = b - 0x57; + else { + zs->ZPktState = Z_PktGetPAD; + return true; + } + + if (zs->HexLo) { + zs->PktIn[zs->PktInPtr] = + zs->PktIn[zs->PktInPtr] + b; + zs->HexLo = false; + zs->PktInPtr++; + zs->PktInCount--; + if (zs->PktInCount <= 0) { + zs->ZPktState = Z_PktGetHexEOL; + zs->PktInCount = 2; + } + } else { + zs->PktIn[zs->PktInPtr] = b << 4; + zs->HexLo = true; + } + break; + case Z_PktGetHexEOL: + zs->PktInCount--; + if (zs->PktInCount <= 0) { + zs->ZPktState = Z_PktGetPAD; + if (ZCheckHdr(zs)) + ZParseHdr(zs); + } + break; + case Z_PktGetData: + switch (b) { + case ZDLE: + zs->Quoted = true; + break; + default: + if (zs->Quoted) { + switch (b) { + case ZCRCE: + case ZCRCG: + case ZCRCQ: + case ZCRCW: + zs->TERM = b; + if (zs->CRC32) + zs->PktInCount = 4; + else + zs->PktInCount = 2; + zs->ZPktState = Z_PktGetCRC; + break; + case ZRUB0: + b = 0x7F; + break; + case ZRUB1: + b = 0xFF; + break; + default: + b = b ^ 0x40; + } + zs->Quoted = false; + } + if (zs->CRC32) + zs->CRC3 = UpdateCRC32(b, zs->CRC3); + else + zs->CRC = UpdateCRC(b, zs->CRC); + if (zs->ZPktState == Z_PktGetData) { + if (zs->PktInPtr < 1024) { + zs->PktIn[zs->PktInPtr] = b; + zs->PktInPtr++; + } else + zs->ZPktState = Z_PktGetPAD; + } + } + break; + case Z_PktGetCRC: + switch (b) { + case ZDLE: + zs->Quoted = true; + break; + default: + if (zs->Quoted) { + switch (b) { + case ZRUB0: + b = 0x7F; + break; + case ZRUB1: + b = 0xFF; + break; + default: + b = b ^ 0x40; + } + zs->Quoted = false; + } + if (zs->CRC32) + zs->CRC3 = UpdateCRC32(b, zs->CRC3); + else + zs->CRC = UpdateCRC(b, zs->CRC); + zs->PktInCount--; + if (zs->PktInCount <= 0) + ZCheckData(zs); + } + break; + } + c = ZRead1Byte(zs, &b); + } + + if (!zs->Sending) + switch (zs->ZState) { + case Z_SendInitHdr: + ZSendInitDat(zs); + break; + case Z_SendFileHdr: + ZSendFileDat(zs); + break; + case Z_SendDataHdr: + case Z_SendDataDat: + ZSendDataDat(zs); + break; + case Z_Cancel: + zs->ZState = Z_End; + break; + } + + if (zs->Sending && (zs->PktOutCount > 0)) + return true; + } while (zs->Sending); + + if (zs->ZState == Z_End) + return false; + + return true; +} + +void +ZCancel(struct zmodem_session *zs) +{ + ZSendCancel(zs); +} + +void +ZDestroy(struct zmodem_session *zs) +{ + if (zs->file) + fclose(zs->file); + if (zs->file_name) + free(zs->file_name); + free(zs); +} --- zmodem.h Mon Jul 4 20:46:10 2022 +++ zmodem.h Mon Jul 4 20:46:10 2022 @@ -0,0 +1,87 @@ +/* + * Copyright (C) 1994-1998 T. Teranishi + * (C) 2009- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ZMODEM_H__ +#define __ZMODEM_H__ + +//#include <stdbool.h> +#include "util.h" + +/* #define ZMODEM_DEBUG */ + +/* ZMODEM function id */ +#define IdZReceive 1 +#define IdZSend 2 +#define IdZAutoR 3 +#define IdZAutoS 4 + +enum { + ZMODEM_MODE, + ZMODEM_BINFLAG +}; + +struct zmodem_session { + void *cookie; + short (*send)(void *cookie, unsigned char *data, size_t size); + short (*recv)(void *cookie, unsigned char *data); + + FILE *file; + unsigned long file_size; + char *file_name; + unsigned long file_mtime; + + unsigned char RxHdr[4], TxHdr[4]; + unsigned char RxType, TERM; + unsigned char PktIn[1032], PktOut[1032]; + short PktInPtr, PktOutPtr; + short PktInCount, PktOutCount; + short PktInLen; + bool BinFlag; + bool Sending; + short ZMode, ZState, ZPktState; + short MaxDataLen, TimeOut, CanCount; + bool CtlEsc, CRC32, HexLo, Quoted, CRRecv; + short CRC; + long CRC3, Pos, LastPos, WinSize; + unsigned char LastSent; + + unsigned long TimeOutAt; +}; + +struct zmodem_session * ZCreateSender(FILE *fp); +struct zmodem_session * ZCreateReceiver(void); + +bool ZInit(struct zmodem_session *zs); +void ZDestroy(struct zmodem_session *zs); +bool ZParse(struct zmodem_session *zs); +void ZCancel(struct zmodem_session *zs); +bool ZHaveTimedOut(struct zmodem_session *zs); +void ZTimeOutProc(struct zmodem_session *zs); + +#endif