blob: 0d742ab2a5e5c9318057732719df905471073694 [file] [log] [blame]
/*
zm.c - zmodem protocol handling lowlevelstuff
Copyright (C) until 1998 Chuck Forsberg (OMEN Technology Inc)
Copyright (C) 1996, 1997 Uwe Ohse
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
originally written by Chuck Forsberg
*/
/* historical comment: -- uwe
* Z M . C
* ZMODEM protocol primitives
* 05-09-88 Chuck Forsberg Omen Technology Inc
*
* Entry point Functions:
* zsbhdr(type, hdr) send binary header
* zshhdr(type, hdr) send hex header
* zgethdr(hdr, eflag) receive header - binary or hex
* zsdata(buf, len, frameend) send data
* zrdata(buf, len, bytes_received) receive data
* stohdr(pos) store position data in Txhdr
* long rclhdr(hdr) recover position offset from header
*/
#include "zglobal.h"
#include <stdio.h>
unsigned int Rxtimeout = 100; /* Tenths of seconds to wait for something */
/* Globals used by ZMODEM functions */
int Rxframeind; /* ZBIN ZBIN32, or ZHEX type of frame received */
int Rxtype; /* Type of header received */
char Rxhdr[4]; /* Received header */
char Txhdr[4]; /* Transmitted header */
long Txpos; /* Transmitted file position */
int Txfcs32; /* TRUE means send binary frames with 32 bit FCS */
int Crc32t; /* Display flag indicating 32 bit CRC being sent */
int Crc32; /* Display flag indicating 32 bit CRC being received */
int Znulls; /* Number of nulls to send at beginning of ZDATA hdr */
char Attn[ZATTNLEN+1]; /* Attention string rx sends to tx on err */
static char lastsent; /* Last char we sent */
int turbo_escape;
int bytes_per_error=0;
static const char *frametypes[] = {
"Carrier Lost", /* -3 */
"TIMEOUT", /* -2 */
"ERROR", /* -1 */
#define FTOFFSET 3
"ZRQINIT",
"ZRINIT",
"ZSINIT",
"ZACK",
"ZFILE",
"ZSKIP",
"ZNAK",
"ZABORT",
"ZFIN",
"ZRPOS",
"ZDATA",
"ZEOF",
"ZFERR",
"ZCRC",
"ZCHALLENGE",
"ZCOMPL",
"ZCAN",
"ZFREECNT",
"ZCOMMAND",
"ZSTDERR",
"xxxxx"
#define FRTYPES 22 /* Total number of frame types in this array */
/* not including psuedo negative entries */
};
#define badcrc _("Bad CRC")
/* static char *badcrc = "Bad CRC"; */
static inline int noxrd7 __P ((void));
static inline int zdlread __P ((void));
static int zdlread2 __P ((int)) LRZSZ_ATTRIB_REGPARM(1);
static inline int zgeth1 __P ((void));
static void zputhex __P ((int c, char *pos));
static inline int zgethex __P ((void));
static int zrbhdr __P ((char *hdr));
static int zrbhdr32 __P ((char *hdr));
static int zrhhdr __P ((char *hdr));
static char zsendline_tab[256];
static int zrdat32 __P ((char *buf, int length, size_t *));
static void zsbh32 __P ((char *hdr, int type));
static inline void zsendline_s __P((const char *s, size_t count));
extern int zmodem_requested;
#define sendline(c) putchar((c) & 0377)
#define xsendline(c) putchar(c)
/*
* Read a character from the modem line with timeout.
* Eat parity, XON and XOFF characters.
*/
static inline int
noxrd7(void)
{
register int c;
for (;;) {
if ((c = READLINE_PF(Rxtimeout)) < 0)
return c;
switch (c &= 0177) {
case XON:
case XOFF:
continue;
default:
if (Zctlesc && !(c & 0140))
continue;
case '\r':
case '\n':
case ZDLE:
return c;
}
}
}
static inline int
zgeth1(void)
{
register int c, n;
if ((c = noxrd7()) < 0)
return c;
n = c - '0';
if (n > 9)
n -= ('a' - ':');
if (n & ~0xF)
return ERROR;
if ((c = noxrd7()) < 0)
return c;
c -= '0';
if (c > 9)
c -= ('a' - ':');
if (c & ~0xF)
return ERROR;
c += (n<<4);
return c;
}
/* Decode two lower case hex digits into an 8 bit byte value */
static inline int
zgethex(void)
{
register int c;
c = zgeth1();
VPRINTF(9,("zgethex: %02X", c));
return c;
}
/*
* Read a byte, checking for ZMODEM escape encoding
* including CAN*5 which represents a quick abort
*/
static inline int
zdlread(void)
{
int c;
/* Quick check for non control characters */
if ((c = READLINE_PF(Rxtimeout)) & 0140)
return c;
return zdlread2(c);
}
/* no, i don't like gotos. -- uwe */
static int
zdlread2(int c)
{
goto jump_over; /* bad style */
again:
/* Quick check for non control characters */
if ((c = READLINE_PF(Rxtimeout)) & 0140)
return c;
jump_over:
switch (c) {
case ZDLE:
break;
case XON:
case (XON|0200):
case XOFF:
case (XOFF|0200):
goto again;
default:
if (Zctlesc && !(c & 0140)) {
goto again;
}
return c;
}
again2:
if ((c = READLINE_PF(Rxtimeout)) < 0)
return c;
if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0)
return c;
if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0)
return c;
if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0)
return c;
switch (c) {
case CAN:
return GOTCAN;
case ZCRCE:
case ZCRCG:
case ZCRCQ:
case ZCRCW:
return (c | GOTOR);
case ZRUB0:
return 0177;
case ZRUB1:
return 0377;
case XON:
case (XON|0200):
case XOFF:
case (XOFF|0200):
goto again2;
default:
if (Zctlesc && ! (c & 0140)) {
goto again2;
}
if ((c & 0140) == 0100)
return (c ^ 0100);
break;
}
VPRINTF(2,(_("Bad escape sequence %x"), c));
return ERROR;
}
/*
* Send character c with ZMODEM escape sequence encoding.
* Escape XON, XOFF. Escape CR following @ (Telenet net escape)
*/
inline void
zsendline(int c)
{
switch(zsendline_tab[(unsigned) (c&=0377)])
{
case 0:
xsendline(lastsent = c);
break;
case 1:
xsendline(ZDLE);
c ^= 0100;
xsendline(lastsent = c);
break;
case 2:
if ((lastsent & 0177) != '@') {
xsendline(lastsent = c);
} else {
xsendline(ZDLE);
c ^= 0100;
xsendline(lastsent = c);
}
break;
}
}
static inline void
zsendline_s(const char *s, size_t count)
{
const char *end=s+count;
while(s!=end) {
int last_esc=0;
const char *t=s;
while (t!=end) {
last_esc=zsendline_tab[(unsigned) ((*t) & 0377)];
if (last_esc)
break;
t++;
}
if (t!=s) {
fwrite(s,(size_t)(t-s),1,stdout);
lastsent=t[-1];
s=t;
}
if (last_esc) {
int c=*s;
switch(last_esc) {
case 0:
xsendline(lastsent = c);
break;
case 1:
xsendline(ZDLE);
c ^= 0100;
xsendline(lastsent = c);
break;
case 2:
if ((lastsent & 0177) != '@') {
xsendline(lastsent = c);
} else {
xsendline(ZDLE);
c ^= 0100;
xsendline(lastsent = c);
}
break;
}
s++;
}
}
}
/* Send ZMODEM binary header hdr of type type */
void
zsbhdr(int type, char *hdr)
{
register int n;
register unsigned short crc;
VPRINTF(3,("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr)));
if (type == ZDATA)
for (n = Znulls; --n >=0; )
xsendline(0);
xsendline(ZPAD); xsendline(ZDLE);
Crc32t=Txfcs32;
if (Crc32t)
zsbh32(hdr, type);
else {
xsendline(ZBIN); zsendline(type); crc = updcrc(type, 0);
for (n=4; --n >= 0; ++hdr) {
zsendline(*hdr);
crc = updcrc((0377& *hdr), crc);
}
crc = updcrc(0,updcrc(0,crc));
zsendline(crc>>8);
zsendline(crc);
}
if (type != ZDATA)
flushmo();
}
/* Send ZMODEM binary header hdr of type type */
static void
zsbh32(char *hdr, int type)
{
register int n;
register unsigned long crc;
xsendline(ZBIN32); zsendline(type);
crc = 0xFFFFFFFFL; crc = UPDC32(type, crc);
for (n=4; --n >= 0; ++hdr) {
crc = UPDC32((0377 & *hdr), crc);
zsendline(*hdr);
}
crc = ~crc;
for (n=4; --n >= 0;) {
zsendline((int)crc);
crc >>= 8;
}
}
/* Send ZMODEM HEX header hdr of type type */
void
zshhdr(int type, char *hdr)
{
register int n;
register unsigned short crc;
char s[30];
size_t len;
VPRINTF(3,("zshhdr: %s %lx", frametypes[(type & 0x7f)+FTOFFSET], rclhdr(hdr)));
s[0]=ZPAD;
s[1]=ZPAD;
s[2]=ZDLE;
s[3]=ZHEX;
zputhex(type & 0x7f ,s+4);
len=6;
Crc32t = 0;
crc = updcrc((type & 0x7f), 0);
for (n=4; --n >= 0; ++hdr) {
zputhex(*hdr,s+len);
len += 2;
crc = updcrc((0377 & *hdr), crc);
}
crc = updcrc(0,updcrc(0,crc));
zputhex(crc>>8,s+len);
zputhex(crc,s+len+2);
len+=4;
/* Make it printable on remote machine */
s[len++]=015;
s[len++]=0212;
/*
* Uncork the remote in case a fake XOFF has stopped data flow
*/
if (type != ZFIN && type != ZACK)
{
s[len++]=021;
}
flushmo();
write(1,s,len);
}
/*
* Send binary array buf of length length, with ending ZDLE sequence frameend
*/
static const char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"};
void
zsdata(const char *buf, size_t length, int frameend)
{
register unsigned short crc;
VPRINTF(3,("zsdata: %lu %s", (unsigned long) length,
Zendnames[(frameend-ZCRCE)&3]));
crc = 0;
do {
zsendline(*buf); crc = updcrc((0377 & *buf), crc);
buf++;
} while (--length>0);
xsendline(ZDLE); xsendline(frameend);
crc = updcrc(frameend, crc);
crc = updcrc(0,updcrc(0,crc));
zsendline(crc>>8); zsendline(crc);
if (frameend == ZCRCW) {
xsendline(XON); flushmo();
}
}
void
zsda32(const char *buf, size_t length, int frameend)
{
int c;
unsigned long crc;
int i;
VPRINTF(3,("zsdat32: %d %s", length, Zendnames[(frameend-ZCRCE)&3]));
crc = 0xFFFFFFFFL;
zsendline_s(buf,length);
for (; length; length--) {
c = *buf & 0377;
crc = UPDC32(c, crc);
buf++;
}
xsendline(ZDLE); xsendline(frameend);
crc = UPDC32(frameend, crc);
crc = ~crc;
for (i=4; --i >= 0;) {
c=(int) crc;
if (c & 0140)
xsendline(lastsent = c);
else
zsendline(c);
crc >>= 8;
}
if (frameend == ZCRCW) {
xsendline(XON); flushmo();
}
}
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ <= 4)
# undef DEBUG_BLOCKSIZE
#endif
#ifdef DEBUG_BLOCKSIZE
struct debug_blocksize {
int size;
long count;
};
struct debug_blocksize blocksizes[]={
{32,0},
{64,0},
{128,0},
{256,0},
{512,0},
{1024,0},
{2048,0},
{4096,0},
{8192,0},
{0,0}
};
static inline void
count_blk(int size)
{
int i;
for (i=0;blocksizes[i].size;i++) {
if (blocksizes[i].size==size) {
blocksizes[i].count++;
return;
}
}
blocksizes[i].count++;
}
static void printout_blocksizes(void) __attribute__((__destructor__));
static void
printout_blocksizes(void)
{
int i;
for (i=0;blocksizes[i].size;i++) {
if (blocksizes[i].count) {
lsyslog(LOG_DEBUG,"%4d byte: %ld blocks\n",
blocksizes[i].size,blocksizes[i].count);
}
}
if (blocksizes[i].count) {
lsyslog(LOG_DEBUG,"unk. byte: %ld blocks",
blocksizes[i].count);
}
}
#define COUNT_BLK(x) count_blk(x)
#else
#define COUNT_BLK(x)
#endif
/*
* Receive array buf of max length with ending ZDLE sequence
* and CRC. Returns the ending character or error code.
* NB: On errors may store length+1 bytes!
*/
int
zrdata(char *buf, int length, size_t *bytes_received)
{
register int c;
register unsigned short crc;
register char *end;
register int d;
*bytes_received=0;
if (Rxframeind == ZBIN32)
return zrdat32(buf, length, bytes_received);
crc = 0; end = buf + length;
while (buf <= end) {
if ((c = zdlread()) & ~0377) {
crcfoo:
switch (c) {
case GOTCRCE:
case GOTCRCG:
case GOTCRCQ:
case GOTCRCW:
{
d = c;
c &= 0377;
crc = updcrc(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = updcrc(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = updcrc(c, crc);
if (crc & 0xFFFF) {
zperr(badcrc);
return ERROR;
}
*bytes_received = length - (end - buf);
COUNT_BLK(*bytes_received);
VPRINTF(3,("zrdata: %lu %s", (unsigned long) (*bytes_received),
Zendnames[(d-GOTCRCE)&3]));
return d;
}
case GOTCAN:
zperr(_("Sender Canceled"));
return ZCAN;
case TIMEOUT:
zperr(_("TIMEOUT"));
return c;
default:
zperr(_("Bad data subpacket"));
return c;
}
}
*buf++ = c;
crc = updcrc(c, crc);
}
zperr(_("Data subpacket too long"));
return ERROR;
}
static int
zrdat32(char *buf, int length, size_t *bytes_received)
{
register int c;
register unsigned long crc;
register char *end;
register int d;
crc = 0xFFFFFFFFL; end = buf + length;
while (buf <= end) {
if ((c = zdlread()) & ~0377) {
crcfoo:
switch (c) {
case GOTCRCE:
case GOTCRCG:
case GOTCRCQ:
case GOTCRCW:
d = c;
c &= 0377;
crc = UPDC32(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = UPDC32(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = UPDC32(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = UPDC32(c, crc);
if ((c = zdlread()) & ~0377)
goto crcfoo;
crc = UPDC32(c, crc);
if (crc != 0xDEBB20E3) {
zperr(badcrc);
return ERROR;
}
*bytes_received = length - (end - buf);
COUNT_BLK(*bytes_received);
VPRINTF(3,("zrdat32: %lu %s", (unsigned long) *bytes_received,
Zendnames[(d-GOTCRCE)&3]));
return d;
case GOTCAN:
zperr(_("Sender Canceled"));
return ZCAN;
case TIMEOUT:
zperr(_("TIMEOUT"));
return c;
default:
zperr(_("Bad data subpacket"));
return c;
}
}
*buf++ = c;
crc = UPDC32(c, crc);
}
zperr(_("Data subpacket too long"));
return ERROR;
}
/*
* Read a ZMODEM header to hdr, either binary or hex.
* eflag controls local display of non zmodem characters:
* 0: no display
* 1: display printing characters only
* 2: display all non ZMODEM characters
* On success, set Zmodem to 1, set Rxpos and return type of header.
* Otherwise return negative on error.
* Return ERROR instantly if ZCRCW sequence, for fast error recovery.
*/
int
zgethdr(char *hdr, int eflag, size_t *Rxpos)
{
register int c, cancount;
unsigned int max_garbage; /* Max bytes before start of frame */
size_t rxpos=0; /* keep gcc happy */
max_garbage = Zrwindow + Baudrate;
Rxframeind = Rxtype = 0;
startover:
cancount = 5;
again:
/* Return immediate ERROR if ZCRCW sequence seen */
switch (c = READLINE_PF(Rxtimeout)) {
case RCDO:
case TIMEOUT:
goto fifi;
case CAN:
gotcan:
if (--cancount <= 0) {
c = ZCAN; goto fifi;
}
switch (c = READLINE_PF(1)) {
case TIMEOUT:
goto again;
case ZCRCW:
c = ERROR;
/* **** FALL THRU TO **** */
case RCDO:
goto fifi;
default:
break;
case CAN:
if (--cancount <= 0) {
c = ZCAN; goto fifi;
}
goto again;
}
/* **** FALL THRU TO **** */
default:
agn2:
if ( --max_garbage == 0) {
zperr(_("Garbage count exceeded"));
return(ERROR);
}
if (eflag && ((c &= 0177) & 0140) && Verbose)
vchar(c);
else if (eflag > 1 && Verbose)
vchar(c);
goto startover;
case ZPAD|0200: /* This is what we want. */
case ZPAD: /* This is what we want. */
break;
}
cancount = 5;
splat:
switch (c = noxrd7()) {
case ZPAD:
goto splat;
case RCDO:
case TIMEOUT:
goto fifi;
default:
goto agn2;
case ZDLE: /* This is what we want. */
break;
}
switch (c = noxrd7()) {
case RCDO:
case TIMEOUT:
goto fifi;
case ZBIN:
Rxframeind = ZBIN; Crc32 = FALSE;
c = zrbhdr(hdr);
break;
case ZBIN32:
Crc32 = Rxframeind = ZBIN32;
c = zrbhdr32(hdr);
break;
case ZHEX:
Rxframeind = ZHEX; Crc32 = FALSE;
c = zrhhdr(hdr);
break;
case CAN:
goto gotcan;
default:
goto agn2;
}
rxpos = hdr[ZP3] & 0377;
rxpos = (rxpos<<8) + (hdr[ZP2] & 0377);
rxpos = (rxpos<<8) + (hdr[ZP1] & 0377);
rxpos = (rxpos<<8) + (hdr[ZP0] & 0377);
fifi:
switch (c) {
case GOTCAN:
c = ZCAN;
/* **** FALL THRU TO **** */
case ZNAK:
case ZCAN:
case ERROR:
case TIMEOUT:
case RCDO:
zperr(_("Got %s"), frametypes[c+FTOFFSET]);
/* **** FALL THRU TO **** */
default:
if (c >= -3 && c <= FRTYPES)
VPRINTF(3,("zgethdr: %s %lx", frametypes[c+FTOFFSET], (unsigned long) rxpos));
else
VPRINTF(3,("zgethdr: %d %lx", c, (unsigned long) rxpos));
}
if (Rxpos)
*Rxpos=rxpos;
return c;
}
/* Receive a binary style header (type and position) */
static int
zrbhdr(char *hdr)
{
register int c, n;
register unsigned short crc;
if ((c = zdlread()) & ~0377)
return c;
Rxtype = c;
crc = updcrc(c, 0);
for (n=4; --n >= 0; ++hdr) {
if ((c = zdlread()) & ~0377)
return c;
crc = updcrc(c, crc);
*hdr = c;
}
if ((c = zdlread()) & ~0377)
return c;
crc = updcrc(c, crc);
if ((c = zdlread()) & ~0377)
return c;
crc = updcrc(c, crc);
if (crc & 0xFFFF) {
zperr(badcrc);
return ERROR;
}
protocol = ZM_ZMODEM;
zmodem_requested=TRUE;
return Rxtype;
}
/* Receive a binary style header (type and position) with 32 bit FCS */
static int
zrbhdr32(char *hdr)
{
register int c, n;
register unsigned long crc;
if ((c = zdlread()) & ~0377)
return c;
Rxtype = c;
crc = 0xFFFFFFFFL; crc = UPDC32(c, crc);
#ifdef DEBUGZ
VPRINTF(3,("zrbhdr32 c=%X crc=%lX", c, crc)i);
#endif
for (n=4; --n >= 0; ++hdr) {
if ((c = zdlread()) & ~0377)
return c;
crc = UPDC32(c, crc);
*hdr = c;
#ifdef DEBUGZ
VPRINTF(3,("zrbhdr32 c=%X crc=%lX", c, crc));
#endif
}
for (n=4; --n >= 0;) {
if ((c = zdlread()) & ~0377)
return c;
crc = UPDC32(c, crc);
#ifdef DEBUGZ
VPRINTF(3,("zrbhdr32 c=%X crc=%lX", c, crc));
#endif
}
if (crc != 0xDEBB20E3) {
zperr(badcrc);
return ERROR;
}
protocol = ZM_ZMODEM;
zmodem_requested=TRUE;
return Rxtype;
}
/* Receive a hex style header (type and position) */
static int
zrhhdr(char *hdr)
{
register int c;
register unsigned short crc;
register int n;
if ((c = zgethex()) < 0)
return c;
Rxtype = c;
crc = updcrc(c, 0);
for (n=4; --n >= 0; ++hdr) {
if ((c = zgethex()) < 0)
return c;
crc = updcrc(c, crc);
*hdr = c;
}
if ((c = zgethex()) < 0)
return c;
crc = updcrc(c, crc);
if ((c = zgethex()) < 0)
return c;
crc = updcrc(c, crc);
if (crc & 0xFFFF) {
zperr(badcrc); return ERROR;
}
switch ( c = READLINE_PF(1)) {
case 0215:
/* **** FALL THRU TO **** */
case 015:
/* Throw away possible cr/lf */
READLINE_PF(1);
break;
}
protocol = ZM_ZMODEM;
zmodem_requested=TRUE;
return Rxtype;
}
/* Write a byte as two hex digits */
static void
zputhex(int c, char *pos)
{
static char digits[] = "0123456789abcdef";
VPRINTF(9,("zputhex: %02X", c));
pos[0]=digits[(c&0xF0)>>4];
pos[1]=digits[c&0x0F];
}
void
zsendline_init(void)
{
int i;
for (i=0;i<256;i++) {
if (i & 0140)
zsendline_tab[i]=0;
else {
switch(i)
{
case ZDLE:
case XOFF: /* ^Q */
case XON: /* ^S */
case (XOFF | 0200):
case (XON | 0200):
zsendline_tab[i]=1;
break;
case 020: /* ^P */
case 0220:
if (turbo_escape)
zsendline_tab[i]=0;
else
zsendline_tab[i]=1;
break;
case 015:
case 0215:
if (Zctlesc)
zsendline_tab[i]=1;
else if (!turbo_escape)
zsendline_tab[i]=2;
else
zsendline_tab[i]=0;
break;
default:
if (Zctlesc)
zsendline_tab[i]=1;
else
zsendline_tab[i]=0;
}
}
}
}
/* Store pos in Txhdr */
void
stohdr(size_t pos)
{
long lpos=(long) pos;
Txhdr[ZP0] = lpos;
Txhdr[ZP1] = lpos>>8;
Txhdr[ZP2] = lpos>>16;
Txhdr[ZP3] = lpos>>24;
}
/* Recover a long integer from a header */
long
rclhdr(char *hdr)
{
long l;
l = (hdr[ZP3] & 0377);
l = (l << 8) | (hdr[ZP2] & 0377);
l = (l << 8) | (hdr[ZP1] & 0377);
l = (l << 8) | (hdr[ZP0] & 0377);
return l;
}
/* End of zm.c */