You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bforce/source/bforce/prot_zmrecv.c

562 lines
13 KiB
C

/*
* binkleyforce -- unix FTN mailer project
*
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
*
* 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 of the License, or
* (at your option) any later version.
*
* $Id$
*/
#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "util.h"
#include "session.h"
#include "outbound.h"
#include "io.h"
#include "prot_common.h"
#include "prot_zmodem.h"
typedef enum zrxstates {
ZRX_INIT, /* Send ZRINIT frame -> goto ZRX_INITACK */
ZRX_INITACK, /* Wait for answer on ZRINIT (for ZRQINIT?) */
ZRX_GOTFILE, /* We got ZFINFO -> check it, open, etc.. */
ZRX_SENDRPOS, /* Send position we want receive file from */
ZRX_RPOSACK, /* Wait for answer on (ZRPOS) our postion */
ZRX_WAITDATA, /* And wait for data block with file's data */
ZRX_REFUSE, /* Feee... Refuse this file */
ZRX_SKIP /* Burn baby.. burn.. skip it! */
} e_zrxstates;
static void zmodem_puts(const char *s);
static int zmodem_proc_ZFILE(s_protinfo *pi, char *blkptr, size_t blklen);
/* ------------------------------------------------------------------------- */
/* Receive files with Z-Modem protocol */
/* Return: */
/* PRC_SUCCESSFUL: All files received or transfer was finished cleanly */
/* PRC_ERROR: Error at sending something(include carrier lost) */
/* PRC_ERROR: Error at receiving something(include carrier lost) */
/* PRC_MERROR: Misc. errors (i.e. incompatibilies, failures, etc) */
/* PRC_TRIESOUT: Very many tries doing something without result */
/* PRC_REMOTEDEAD: For a long time no real information transfered */
/* PRC_REMOTEABORT: Transfer was aborted by remote (i.e. five CAN, etc..) */
/* PRC_LOCALBORT: Transfer was aborted by local (i.e. CPS too low, etc..) */
/* ------------------------------------------------------------------------- */
int rx_zmodem(s_protinfo *pi, bool caller)
{
char zconv; /* ZMODEM file conversion request */
char zmanag; /* ZMODEM file management request */
char ztrans; /* ZMODEM file transport request */
char zexten; /* ZMODEM file extended options */
char *rxbuf; /* Buffer with ZMAXBLOCKLEN size */
int zrqinitcnt; /* Count received ZRQINITs */
int zfincnt; /* Count received ZFINs (in state ZTX_INITACK) */
int inithdrtype; /* Send this header at ZRX_INIT state */
int txtries = 0;
int skipbypos = 0;
int ftype = 0;
enum zrxstates rxstate;
time_t deadtimer;
int rc = PRC_NOERROR;
int n, c;
log("start %s receive", Protocols[state.handshake->protocol]);
DEB((D_PROT, "start %s receive", Protocols[state.handshake->protocol]));
if( pi->start_time == 0 )
pi->start_time = time(NULL);
skipbypos = conf_boolean(cf_zmodem_skip_by_pos);
rxbuf = (char *)xmalloc(ZMAXBLOCKLEN+1);
zrqinitcnt = 0;
zfincnt = 0;
inithdrtype = ZRINIT;
Z_Rxwait = ZWAITTIME;
Z_Rxtout = ZRXTIMEOUT;
rxstate = ZRX_INIT;
setalarm(Z_Rxtout);
timer_set(&deadtimer, ZDEADTIMER);
while(1)
{
if( timer_expired(deadtimer) )
{
DEB((D_PROT, "rx_zmodem: deadtimer = %ld", (long)deadtimer));
DEB((D_PROT, "rx_zmodem: rxstate = %d", (int)rxstate));
DEB((D_PROT, "rx_zmodem: rc = %d", rc));
DEB((D_PROT, "rx_zmodem: txtries = %d", txtries));
log("brain dead! (abort)");
gotoexit(PRC_LOCALABORTED);
}
if( rxstate == ZRX_INIT || rxstate == ZRX_SENDRPOS )
{
if( ++txtries > ZMAXTRIES )
{
log("tries reached miximal count");
gotoexit(PRC_LOCALABORTED);
}
}
switch(rxstate) {
case ZRX_INIT:
DEB((D_PROT, "rx_zmodem: entering state ZRX_INIT"));
stohdr(Z_Txhdr, 0L);
Z_Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
if( Z_Ctlesc )
Z_Txhdr[ZF0] |= TESCCTL;
if( zshhdr(inithdrtype, Z_Txhdr) < 0 )
gotoexit(PRC_ERROR);
if( inithdrtype == ZSKIP )
inithdrtype = ZRINIT;
setalarm(Z_Rxtout);
rxstate = ZRX_INITACK;
break;
case ZRX_GOTFILE:
DEB((D_PROT, "rx_zmodem: entering state ZRX_GOTFILE"));
if( pi->recv && pi->recv->fp )
p_rx_fclose(pi);
switch(zmodem_proc_ZFILE(pi, rxbuf, Z_Rxcount)) {
case 0:
txtries = 0;
rxstate = ZRX_SENDRPOS;
break;
case 2:
rxstate = ZRX_SKIP;
break;
default:
rxstate = ZRX_REFUSE;
break;
}
break;
case ZRX_SENDRPOS:
DEB((D_PROT, "rx_zmodem: entering state ZRX_SENDRPOS"));
stohdr(Z_Txhdr, pi->recv->bytes_received);
if( zshhdr(ZRPOS, Z_Txhdr) < 0 )
gotoexit(PRC_ERROR);
setalarm(Z_Rxtout);
rxstate = ZRX_RPOSACK;
break;
case ZRX_WAITDATA:
DEB((D_PROT, "rx_zmodem: entering state ZRX_WAITDATA"));
if( (rc = p_info(pi, 0)) )
gotoexit(rc);
setalarm(Z_Rxtout);
switch(c = zrdata(rxbuf, ZMAXBLOCKLEN, pi->recv->bytes_received)) {
case ZHANGUP:
case ZEXIT:
gotoexit(PRC_ERROR);
break;
case ZCAN:
gotoexit(PRC_REMOTEABORTED);
break;
case ZTIMER:
log("time out waiting for data");
txtries = 0;
rxstate = ZRX_SENDRPOS;
break;
case ZERROR:
case ZCRCERR: /* CRC error */
zmodem_puts(Z_Attn);
txtries = 0;
rxstate = ZRX_SENDRPOS;
break;
case GOTCRCW:
case GOTCRCQ:
case GOTCRCG:
case GOTCRCE:
timer_set(&deadtimer, ZDEADTIMER);
if( (n = p_rx_writefile(rxbuf, Z_Rxcount, pi)) < 0 )
{
rxstate = ( n == -2 ) ? ZRX_SKIP : ZRX_REFUSE;
break;
}
pi->recv->bytes_received += Z_Rxcount;
switch(c) {
case GOTCRCW:
stohdr(Z_Txhdr, pi->recv->bytes_received);
if( zshhdr(ZACK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
rxstate = ZRX_RPOSACK;
break;
case GOTCRCQ:
stohdr(Z_Txhdr, pi->recv->bytes_received);
if( zshhdr(ZACK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
break;
case GOTCRCG:
break;
case GOTCRCE:
txtries = 0;
rxstate = ZRX_RPOSACK;
break;
}
break;
}
break;
case ZRX_REFUSE:
DEB((D_PROT, "rx_zmodem: entering state ZRX_REFUSE"));
txtries = 0;
inithdrtype = ZFERR;
rxstate = ZRX_INIT;
break;
case ZRX_SKIP:
DEB((D_PROT, "rx_zmodem: entering state ZRX_SKIP"));
txtries = 0;
if( skipbypos )
{
pi->recv->bytes_received = pi->recv->bytes_total;
rxstate = ZRX_SENDRPOS;
}
else
{
inithdrtype = ZSKIP;
rxstate = ZRX_INIT;
}
break;
default:
break;
} /* end of switch(rxstate) */
if( rxstate != ZRX_INIT && rxstate != ZRX_GOTFILE
&& rxstate != ZRX_SENDRPOS && rxstate != ZRX_WAITDATA
&& rxstate != ZRX_SKIP && rxstate != ZRX_REFUSE )
{
switch( ftype = zgethdr(Z_Rxhdr) ) {
case ZCAN:
gotoexit(PRC_REMOTEABORTED);
break;
case ZHANGUP:
case ZEXIT:
gotoexit(PRC_ERROR);
break;
case ZTIMER:
log("time out");
switch(rxstate) {
case ZRX_INITACK: rxstate = ZRX_INIT; break;
case ZRX_RPOSACK: rxstate = ZRX_SENDRPOS; break;
default: break;
}
break;
case ZERROR:
case ZCRCERR:
/* NAK them all! (TODO: think a little) */
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZNAK, Z_Txhdr) < 0 )
gotoexit(PRC_ERROR);
break;
case ZRQINIT:
if( rxstate == ZRX_INITACK )
{
zrqinitcnt++;
rxstate = ZRX_INIT;
}
break;
case ZFILE:
if( rxstate == ZRX_INITACK
|| rxstate == ZRX_RPOSACK )
{
zconv = Z_Rxhdr[ZF0];
zmanag = Z_Rxhdr[ZF1];
ztrans = Z_Rxhdr[ZF2];
zexten = Z_Rxhdr[ZF3];
/* default to "binary" mode */
if( !zconv )
zconv = ZCBIN;
inithdrtype = ZRINIT;
setalarm(Z_Rxtout);
switch(zrdata(rxbuf, ZMAXBLOCKLEN, 0)) {
case ZHANGUP:
case ZEXIT:
gotoexit(PRC_ERROR);
break;
case ZTIMER:
txtries = 0;
rxstate = ZRX_INIT;
break;
case GOTCRCW:
rxstate = ZRX_GOTFILE;
break;
default:
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZNAK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
rxstate = ZRX_INITACK;
break;
}
}
break;
case ZSINIT:
if( rxstate == ZRX_INITACK )
{
Z_Ctlesc = TESCCTL & Z_Rxhdr[ZF0];
setalarm(Z_Rxtout);
switch( zrdata(Z_Attn, ZATTNLEN, 0) ) {
case ZHANGUP:
case ZEXIT:
gotoexit(PRC_ERROR);
break;
case ZTIMER:
txtries = 0;
rxstate = ZRX_INIT;
break;
case GOTCRCW:
stohdr(Z_Txhdr, 1L);
if( zshhdr(ZACK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
rxstate = ZRX_INIT;
break;
default:
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZNAK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
break;
}
}
break;
case ZNAK:
switch(rxstate) {
case ZRX_INITACK: rxstate = ZRX_INIT; break;
case ZRX_RPOSACK: rxstate = ZRX_SENDRPOS; break;
default: break;
}
break;
case ZEOF:
if( rxstate == ZRX_RPOSACK )
{
if( rclhdr(Z_Rxhdr) == pi->recv->bytes_received )
{
pi->recv->status = FSTAT_SUCCESS;
if( !p_rx_fclose(pi) )
{
rxstate = ZRX_INIT;
inithdrtype = ZRINIT;
} else
rxstate = ZRX_REFUSE;
txtries = 0;
} else
log("out of sync");
}
break;
case ZSKIP:
if( rxstate == ZRX_RPOSACK )
{
log("remote side skipped file");
rxstate = ZRX_INIT;
}
break;
case ZDATA:
if( rxstate == ZRX_RPOSACK )
{
if( rclhdr(Z_Rxhdr) == pi->recv->bytes_received )
{
rxstate = ZRX_WAITDATA;
}
else
{
log("out of sync");
zmodem_puts(Z_Attn);
txtries = 0;
rxstate = ZRX_SENDRPOS;
}
}
break;
case ZFREECNT:
if( rxstate == ZRX_INITACK )
{
stohdr(Z_Txhdr, ~0L);
if( zshhdr(ZACK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
}
break;
case ZCOMMAND:
if( rxstate == ZRX_INITACK )
{
inithdrtype = ZRINIT;
setalarm(Z_Rxtout);
switch( zrdata(rxbuf, ZMAXBLOCKLEN, 0) ) {
case ZHANGUP:
case ZEXIT:
gotoexit(PRC_ERROR);
break;
case ZTIMER:
txtries = 0;
rxstate = ZRX_INIT;
break;
case GOTCRCW:
log("remote command execution requested (n/a)");
log("command: \"%s\"", rxbuf);
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZCOMPL, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
gotoexit(PRC_LOCALABORTED);
break;
default:
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZNAK, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
txtries = 0;
rxstate = ZRX_INIT;
break;
}
}
break;
case ZFIN:
if( zrqinitcnt )
{
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZFIN, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
gotoexit(PRC_NOERROR);
}
else if( rxstate == ZRX_INITACK )
{
/*
* Don't believe first ZFIN on outgoing calls
*/
if( ++zfincnt > ZRXSKIPFIN || !caller )
{
stohdr(Z_Txhdr, 0L);
if( zshhdr(ZFIN, Z_Txhdr) < 0 ) gotoexit(PRC_ERROR);
gotoexit(PRC_NOERROR);
}
rxstate = ZRX_INIT;
}
break;
default:
log("got unexpected frame %d", ftype);
break;
}
}
} /* end of while(1) */
exit:
DEB((D_PROT, "rx_zmodem: RECV exit = %d", rc));
setalarm(0);
if( pi->recv && pi->recv->fp )
p_rx_fclose(pi);
if( rxbuf )
free(rxbuf);
return(rc);
}
/* ------------------------------------------------------------------------- */
/* Send a string to the modem, processing for \336 (sleep 1 sec) */
/* and \335 (break signal) */
/* ------------------------------------------------------------------------- */
static void zmodem_puts(const char *s)
{
DEB((D_PROT, "zmodem_puts: \"%s\"", string_printable(s)));
while( s && *s )
{
char *p = strpbrk(s, "\335\336");
if( !p )
{
WRITE_TIMEOUT(s, strlen(s));
return;
}
if( p != s )
{
WRITE_TIMEOUT(s, p-s);
s = p;
}
if( *p == '\336' )
sleep(1);
else
tio_send_break();
p++;
}
}
/* ------------------------------------------------------------------------- */
/* Process incoming file information header */
/* ------------------------------------------------------------------------- */
static int zmodem_proc_ZFILE(s_protinfo *pi, char *blkptr, size_t blklen)
{
char *fileiptr = NULL;
size_t filesize = 0L;
time_t filetime = 0L;
#ifdef DEBUG
char *tmp = string_printable_buffer(blkptr, blklen);
DEB((D_PROT, "zmodem_proc_ZFILE: process \"%s\" (%ld bytes)",
tmp, blklen));
if( tmp )
{
free(tmp); tmp = NULL;
}
#endif
blkptr[blklen] = '\0';
fileiptr = blkptr + strlen(blkptr) + 1;
if( fileiptr >= (blkptr + blklen) ||
sscanf(fileiptr, "%d%lo", &filesize, (unsigned long *)&filetime) < 1 )
{
log("zmodem: got invalid ZFILE packet");
return 1;
}
DEB((D_PROT, "zmodem_proc_ZFILE: filesize=%ld, filetime=%lo",
(long)filesize, (long)filetime));
return p_rx_fopen(pi, blkptr, filesize, filetime, 0);
}