/* * Copyright (c) 2022 joshua stein * * 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 #include #include #include #include #include "crc.h" #include "util.h" #include "zmodem.h" /* #define ZMODEM_DEBUG */ #define ZMODEM_TIMEOUT 20 #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 #define IAC 0xff 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 ZCreateTemporaryFile(struct zmodem_session *); bool ZParseFile(struct zmodem_session *); bool ZWriteData(struct zmodem_session *); void ZCheckData(struct zmodem_session *); #ifdef ZMODEM_DEBUG 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]); return ""; } 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]); return ""; } #endif /* ZMODEM_DEBUG */ struct zmodem_session * ZCreateSender(struct session *session, FILE *fp, char *file_name) { struct zmodem_session *zs; if (!fp) return NULL; zs = xmalloczero(sizeof(struct zmodem_session)); if (zs == NULL) return NULL; zs->session = session; zs->file = fp; strlcpy(zs->file_name, file_name, sizeof(zs->file_name)); fseek(zs->file, 0, SEEK_END); zs->file_size = ftell(zs->file); fseek(zs->file, 0, SEEK_SET); zs->BinFlag = true; zs->ZMode = IdZAutoS; return zs; } struct zmodem_session * ZCreateReceiver(struct session *session, char *path) { struct zmodem_session *zs; zs = xmalloczero(sizeof(struct zmodem_session)); if (zs == NULL) return NULL; zs->session = session; zs->ZMode = IdZAutoR; zs->upload_file_path = xstrdup(path); if (zs->upload_file_path == NULL) { xfree(&zs); return NULL; } return zs; } short ZRead1Byte(struct zmodem_session *zs, unsigned char *b) { if (zs->DoIACEscape && zs->LastIAC != 0 && zs->LastIAC != IAC) { *b = zs->LastIAC; zs->LastIAC = 0; return 1; } if (session_get_char(zs->session, b) == 0) return 0; if (zs->DoIACEscape && zs->LastIAC) { if (*b == IAC) { zs->LastIAC = 0; return 1; } /* * The IAC was not escaping an IAC, so return IAC this time and * save the byte we just read for the next iteration. */ zs->LastIAC = *b; *b = IAC; return 1; } if (zs->DoIACEscape && *b == IAC) { zs->LastIAC = IAC; 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 + seconds; } bool ZHaveTimedOut(struct zmodem_session *zs) { if (zs->TimeOutAt && Time >= 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] = 0x8D; zs->PktOutCount++; zs->PktOut[zs->PktOutCount] = 0x8A; zs->PktOutCount++; if ((HdrType != ZFIN) && (HdrType != ZACK)) { zs->PktOut[zs->PktOutCount] = XON; zs->PktOutCount++; } zs->PktOutPtr = 0; zs->Sending = true; #ifdef ZMODEM_DEBUG logger_printf("[%s] ZShHdr: %s", state_name(zs), hdrtype_name(HdrType)); #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; case 0xff: if (zs->DoIACEscape) { zs->PktOut[*i] = 0xff; (*i)++; } /* FALLTHROUGH */ 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 logger_printf("[%s] ZSbHdr: %s", state_name(zs), 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; /* we only want this much data thrown at us before we ack it */ ZStoHdr(zs, ZMODEM_BLOCK_SIZE * 2); zs->TxHdr[ZF0] = CANFC32 | CANFDX; 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 logger_printf("[%s] ZSendCancel", state_name(zs)); #endif } void ZSendInitHdr(struct zmodem_session *zs) { zs->ZState = Z_SendInitHdr; ZStoHdr(zs, 0); if (zs->CtlEsc) zs->TxHdr[ZF0] = ESCCTL; ZShHdr(zs, ZSINIT); } 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 logger_printf("[%s] ZSendInitDat", state_name(zs)); #endif } void ZSendFileHdr(struct zmodem_session *zs) { zs->ZState = Z_SendFileHdr; ZStoHdr(zs, 0); if (zs->BinFlag) zs->TxHdr[ZF0] = ZCBIN; /* binary file */ else zs->TxHdr[ZF0] = ZCNL; /* text file, convert newline */ ZSbHdr(zs, ZFILE); #ifdef ZMODEM_DEBUG logger_printf("[%s] ZSendFileHdr: %s", state_name(zs), zs->BinFlag ? "binary" : "text"); #endif } void ZSendFileDat(struct zmodem_session *zs) { short i, j; if (!zs->file) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZSendFileDat: no file, canceling", state_name(zs)); #endif 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) */ snprintf((char *)zs->PktOut + zs->PktOutCount, sizeof(zs->PktOut) - zs->PktOutCount, "%lu 0 100644 0 0", zs->file_size); 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 logger_printf("[%s] ZSendFileDat: ZFILE: ZF0=%x ZF1=%x " "ZF2=%x file=%s size=%lu", state_name(zs), zs->TxHdr[ZF0], zs->TxHdr[ZF1],zs->TxHdr[ZF2], zs->file_name, zs->file_size); #endif } void ZSendDataHdr(struct zmodem_session *zs) { zs->ZState = Z_SendDataHdr; ZStoHdr(zs, zs->Pos); ZSbHdr(zs, ZDATA); } void ZSendDataDat(struct zmodem_session *zs) { short c; unsigned char b; if (zs->Pos >= zs->file_size) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZSendDataDat: pos %lu >= file size %lu, EOFing", state_name(zs), zs->Pos, zs->file_size); #endif 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; ZResetTimeout(zs, zs->TimeOut); #ifdef ZMODEM_DEBUG logger_printf("[%s] ZSendDataDat: pos=%ld", state_name(zs), zs->Pos); #endif } bool ZInit(struct zmodem_session *zs) { zs->CtlEsc = false; zs->MaxDataLen = ZMODEM_BLOCK_SIZE; zs->WinSize = 32767; if (zs->ZMode == IdZAutoR || zs->ZMode == IdZAutoS) { if (zs->ZMode == IdZAutoR) { zs->ZMode = IdZReceive; session_output(zs->session, (const char *)'0', 1); } else { zs->ZMode = IdZSend; session_output(zs->session, (const char *)'1', 1); } session_output(zs->session, (const char *)'0', 1); session_output(zs->session, (const char *)'B', 1); session_output(zs->session, (const char *)ZDLE, 1); session_output(zs->session, (const 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 = ZMODEM_BLOCK_SIZE; if (zs->MaxDataLen < 64) zs->MaxDataLen = 64; if (zs->MaxDataLen > ZMODEM_BLOCK_SIZE) zs->MaxDataLen = ZMODEM_BLOCK_SIZE; zs->TimeOut = ZMODEM_TIMEOUT; ZResetTimeout(zs, zs->TimeOut); #ifdef ZMODEM_DEBUG logger_printf("[%s] ZInit: %s", state_name(zs), zs->ZMode == IdZReceive ? "receive" : "send"); #endif switch (zs->ZMode) { case IdZReceive: zs->ZState = Z_RecvInit; /* give the user extra time to pick a file */ ZResetTimeout(zs, zs->TimeOut * 3); ZSendRInit(zs); break; case IdZSend: zs->ZState = Z_SendInit; ZSendRQInit(zs); break; } return true; } void ZTimeOutProc(struct zmodem_session *zs) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZTimeOutProc", state_name(zs)); #endif 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) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCheckHdr: CRC%s ok", state_name(zs), zs->CRC32 ? "32" : "16"); #endif } else { #ifdef ZMODEM_DEBUG if (zs->CRC32) logger_printf("[%s] ZCheckHdr: CRC32 check failed " "(0x%lx != 0xDEBB20E3)", state_name(zs), zs->CRC3); else logger_printf("[%s] ZCheckHdr: CRC16 check failed " "(0x%x != 0x0)", state_name(zs), zs->CRC); #endif 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)) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseRInit: not in Init or EOF", state_name(zs)); #endif return; } if (!zs->file) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseRInit: no file, FINing", state_name(zs)); #endif 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 = ZMODEM_BLOCK_SIZE; if (zs->MaxDataLen > max) zs->MaxDataLen = max; #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseRInit: max data len %d", state_name(zs), zs->MaxDataLen); #endif zs->ZState = Z_SendFileHdr; ZSendFileHdr(zs); } bool ZParseSInit(struct zmodem_session *zs) { if (zs->ZState != Z_RecvInit) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseSInit: state not RecvInit", state_name(zs)); #endif 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 logger_printf("[%s] ZParseHdr: RxType %s", state_name(zs), 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) { #if 0 zs->ZState = Z_RecvFIN; #else /* we only want to receive one file per session, just end */ zs->ZState = Z_End; #endif ZSendFIN(zs); zs->CanCount = 2; ZResetTimeout(zs, zs->TimeOut); } else { zs->ZState = Z_End; session_output(zs->session, (const 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 logger_printf("[%s] ZParseHdr: pos=%ld", state_name(zs), zs->Pos); #endif ZSendDataHdr(zs); break; } break; case ZDATA: if (zs->Pos != ZRclHdr(zs)) { ZSendRPOS(zs); return; } ZResetTimeout(zs, zs->TimeOut); zs->ZPktState = Z_PktGetData; break; case ZEOF: if (zs->Pos != ZRclHdr(zs)) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseHdr: ZEOF but pos %ld != received %ld", state_name(zs), zs->Pos, ZRclHdr(zs)); #endif ZSendRPOS(zs); return; } if (zs->file) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseHdr: finished with file at %ld, " "closing", state_name(zs), zs->Pos); #endif ZSendRPOS(zs); if (zs->CRRecv) { zs->CRRecv = false; fwrite("\r", 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 ZCreateTemporaryFile(struct zmodem_session *zs) { if (zs->upload_file_path[0] == '\0') { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCreateTemporaryFile: no file path", state_name(zs)); #endif return false; } zs->file = fopen(zs->upload_file_path, "wb+"); if (zs->file == NULL) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCreateTemporaryFile: opening %s failed", state_name(zs), zs->upload_file_path); #endif return false; } #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCreateTemporaryFile: %s", state_name(zs), zs->upload_file_path); #endif 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)) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseFile: state not RecvInit/2", state_name(zs)); #endif return false; } zs->CRRecv = false; /* file name */ zs->PktIn[zs->PktInPtr] = '\0'; /* don't allow paths in file name */ tfile_name = (char *)&zs->PktIn; i = 0; while (tfile_name[i] != '\0') { if (tfile_name[i] == '/') { tfile_name += i + 1; i = 0; } i++; } #ifdef ZMODEM_DEBUG logger_printf("[%s] ZParseFile: path \"%s\" -> \"%s\"", state_name(zs), (char *)&zs->PktIn, tfile_name); #endif if (tfile_name[0] == '\0') return false; strlcpy(zs->file_name, tfile_name, sizeof(zs->file_name)); if (!ZCreateTemporaryFile(zs)) { zs->file_name[0] = '\0'; 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); zs->ZState = Z_RecvData; ZStoHdr(zs, 0); /* 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) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZWriteData: state != RecvData", state_name(zs)); #endif return false; } 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); } #ifdef ZMODEM_DEBUG logger_printf("[%s] ZWriteData: wrote %d byte(s) at %ld", state_name(zs), zs->PktInPtr, zs->Pos); #endif 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)) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCheckData: CRC%s failed", state_name(zs), zs->CRC32 ? "32" : "16"); #endif 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) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCheckData: bad RxType %s, skipping", state_name(zs), hdrtype_name(zs->RxType)); #endif 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; } #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCheckData: TERM %s", state_name(zs), hdrtype_name(zs->TERM)); #endif 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 = session_output(zs->session, (const char *)&(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 < ZMODEM_BLOCK_SIZE) { 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) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZCancel", state_name(zs)); #endif ZSendCancel(zs); } void ZDestroy(struct zmodem_session *zs) { #ifdef ZMODEM_DEBUG logger_printf("[%s] ZDestroy", state_name(zs)); #endif if (zs->file) fclose(zs->file); if (zs->upload_file_path) xfree(&zs->upload_file_path); xfree(&zs); }