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.
2137 lines
47 KiB
C
2137 lines
47 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"
|
|
|
|
typedef enum hydra_pkttype {
|
|
HPKT_EXIT = -3,
|
|
HPKT_JUNK = -2,
|
|
HPKT_NONE = -1,
|
|
HPKT_START = 'A',
|
|
HPKT_INIT = 'B',
|
|
HPKT_INITACK = 'C',
|
|
HPKT_FINFO = 'D',
|
|
HPKT_FINFOACK = 'E',
|
|
HPKT_DATA = 'F',
|
|
HPKT_DATAACK = 'G',
|
|
HPKT_RPOS = 'H',
|
|
HPKT_EOF = 'I',
|
|
HPKT_EOFACK = 'J',
|
|
HPKT_END = 'K',
|
|
HPKT_IDLE = 'L',
|
|
HPKT_DEVDATA = 'M',
|
|
HPKT_DEVDACK = 'N',
|
|
HPKT_MIN = HPKT_START,
|
|
HPKT_MAX = HPKT_DEVDACK
|
|
} e_hydra_pkttype;
|
|
|
|
typedef enum hydra_char {
|
|
HCHR_PKTEND = 'a',
|
|
HCHR_BINPKT = 'b',
|
|
HCHR_HEXPKT = 'c',
|
|
HCHR_ASCPKT = 'd',
|
|
HCHR_UUEPKT = 'e',
|
|
HCHR_MIN = HCHR_PKTEND,
|
|
HCHR_MAX = HCHR_UUEPKT
|
|
} e_hydra_char;
|
|
|
|
typedef enum hydra_txstate {
|
|
HTX_DONE,
|
|
HTX_START,
|
|
HTX_SWAIT,
|
|
HTX_INIT,
|
|
HTX_INITACK,
|
|
HTX_RINIT,
|
|
HTX_NextFile,
|
|
HTX_ToFName,
|
|
HTX_FINFO,
|
|
HTX_FINFOACK,
|
|
HTX_DATA,
|
|
HTX_SkipFile,
|
|
HTX_DATAACK,
|
|
HTX_XWAIT,
|
|
HTX_EOF,
|
|
HTX_EOFACK,
|
|
HTX_REND,
|
|
HTX_END,
|
|
HTX_ENDACK,
|
|
HTX_Abort
|
|
} e_hydra_txstate;
|
|
|
|
typedef enum hydra_rxstate {
|
|
HRX_DONE,
|
|
HRX_INIT,
|
|
HRX_FINFO,
|
|
HRX_DATA,
|
|
HRX_RPOS,
|
|
HRX_RPosAck,
|
|
HRX_Skip,
|
|
HRX_SkipAck
|
|
} e_hydra_rxstate;
|
|
|
|
char *hydra_pkttype_names[] =
|
|
{
|
|
"HPKT_START",
|
|
"HPKT_INIT",
|
|
"HPKT_INITACK",
|
|
"HPKT_FINFO",
|
|
"HPKT_FINFOACK",
|
|
"HPKT_DATA",
|
|
"HPKT_DATAACK",
|
|
"HPKT_RPOS",
|
|
"HPKT_EOF",
|
|
"HPKT_EOFACK",
|
|
"HPKT_END",
|
|
"HPKT_IDLE",
|
|
"HPKT_DEVDATA"
|
|
};
|
|
|
|
char *hydra_char_names[] =
|
|
{
|
|
"HCHR_PKTEND",
|
|
"HCHR_BINPKT",
|
|
"HCHR_HEXPKT",
|
|
"HCHR_ASCPKT",
|
|
"HCHR_UUEPKT"
|
|
};
|
|
|
|
char *hydra_txstate_names[] =
|
|
{
|
|
"HTX_DONE",
|
|
"HTX_START",
|
|
"HTX_SWAIT",
|
|
"HTX_INIT",
|
|
"HTX_INITACK",
|
|
"HTX_RINIT",
|
|
"HTX_NextFile",
|
|
"HTX_ToFName",
|
|
"HTX_FINFO",
|
|
"HTX_FINFOACK",
|
|
"HTX_DATA",
|
|
"HTX_SkipFile",
|
|
"HTX_DATAACK",
|
|
"HTX_XWAIT",
|
|
"HTX_EOF",
|
|
"HTX_EOFACK",
|
|
"HTX_REND",
|
|
"HTX_END",
|
|
"HTX_ENDACK",
|
|
"HTX_Abort"
|
|
};
|
|
|
|
char *hydra_rxstate_names[] =
|
|
{
|
|
"HRX_DONE",
|
|
"HRX_INIT",
|
|
"HRX_FINFO",
|
|
"HRX_DATA",
|
|
"HRX_RPOS",
|
|
"HRX_RPosAck",
|
|
"HRX_Skip",
|
|
"HRX_SkipAck"
|
|
};
|
|
|
|
#define HYDRA_DLE ('X' - '@')
|
|
#define HYDRA_MINBLKLEN 64
|
|
#define HYDRA_MAXBLKLEN 2048
|
|
#define HYDRA_OVERHEAD 8
|
|
#define HYDRA_MAXDATALEN (HYDRA_MAXBLKLEN + HYDRA_OVERHEAD + 5)
|
|
#define HYDRA_MAXPKTLEN ((HYDRA_MAXBLKLEN + HYDRA_OVERHEAD + 5) * 3)
|
|
#define HYDRA_BUFLEN (HYDRA_MAXPKTLEN + 16)
|
|
#define HYDRA_PKTPREFIX 31
|
|
|
|
#define HOPT_XONXOFF 0x00000001L
|
|
#define HOPT_TELENET 0x00000002L
|
|
#define HOPT_CTLCHRS 0x00000004L
|
|
#define HOPT_HIGHCTL 0x00000008L
|
|
#define HOPT_HIGHBIT 0x00000010L
|
|
#define HOPT_CANBRK 0x00000020L
|
|
#define HOPT_CANASC 0x00000040L
|
|
#define HOPT_CANUUE 0x00000080L
|
|
#define HOPT_CRC32 0x00000100L
|
|
#define HOPT_DEVICE 0x00000200L
|
|
#define HOPT_FPT 0x00000400L
|
|
|
|
/* What we can do */
|
|
#define HYDRA_CANOPT (HOPT_XONXOFF | HOPT_TELENET | HOPT_CTLCHRS | HOPT_HIGHCTL | HOPT_HIGHBIT | HOPT_CANASC | HOPT_CANUUE | HOPT_CRC32)
|
|
/* Vital options if we ask for any; abort if other side doesn't support them */
|
|
#define HYDRA_NECOPT (HOPT_XONXOFF | HOPT_TELENET | HOPT_CTLCHRS | HOPT_HIGHCTL | HOPT_HIGHBIT | HOPT_CANBRK)
|
|
/* Default options */
|
|
#define HYDRA_DEFOPT (0L | HOPT_CRC32)
|
|
/* rxoptions during init (needs to handle ANY link yet unknown at that point */
|
|
#define HYDRA_RXIOPT (HOPT_XONXOFF | HOPT_TELENET | HOPT_CTLCHRS | HOPT_HIGHCTL | HOPT_HIGHBIT)
|
|
/* ditto, but this time txoptions */
|
|
#define HYDRA_TXIOPT (HOPT_XONXOFF | HOPT_TELENET | HOPT_CTLCHRS | HOPT_HIGHCTL | HOPT_HIGHBIT)
|
|
|
|
#define HYDRA_UUENC(c) (((c) & 0x3f) + '!')
|
|
#define HYDRA_UUDEC(c) (((c) - '!') & 0x3f)
|
|
#define HYDRA_ISUUE(c) ((c) > ' ' && (c) < 'a')
|
|
|
|
|
|
#ifdef DEBUG
|
|
#define HYDRA_TXSTATE(c) (hydra_txstate_names[c])
|
|
#define HYDRA_RXSTATE(c) (hydra_rxstate_names[c])
|
|
#define HYDRA_PKTNAME(c) ((c < HPKT_MIN || c > HPKT_MAX) ? "Out of range" : hydra_pkttype_names[c - HPKT_MIN])
|
|
#define HYDRA_CHRNAME(c) ((c < HCHR_MIN || c > HCHR_MAX) ? "Out of range" : hydra_char_names[c - HCHR_MIN])
|
|
#else
|
|
#define HYDRA_TXSTATE(c) ("No debug information")
|
|
#define HYDRA_RXSTATE(c) ("No debug information")
|
|
#define HYDRA_PKTNAME(c) ("No debug information")
|
|
#define HYDRA_CHRNAME(c) ("No debug information")
|
|
#endif
|
|
|
|
typedef struct {
|
|
const char *str;
|
|
unsigned long val;
|
|
size_t len;
|
|
} s_hydraflag;
|
|
|
|
typedef struct {
|
|
bool sent;
|
|
enum hydra_pkttype type;
|
|
char *data;
|
|
size_t size;
|
|
} s_hydrapkt;
|
|
|
|
s_hydraflag hydraflags[] =
|
|
{
|
|
{ "XON", HOPT_XONXOFF, 3 },
|
|
{ "TLN", HOPT_TELENET, 3 },
|
|
{ "CTL", HOPT_CTLCHRS, 3 },
|
|
{ "HIC", HOPT_HIGHCTL, 3 },
|
|
{ "HI8", HOPT_HIGHBIT, 3 },
|
|
{ "BRK", HOPT_CANBRK, 3 },
|
|
{ "ASC", HOPT_CANASC, 3 },
|
|
{ "UUE", HOPT_CANUUE, 3 },
|
|
{ "C32", HOPT_CRC32, 3 },
|
|
{ "DEV", HOPT_DEVICE, 3 },
|
|
{ "FPT", HOPT_FPT, 3 },
|
|
{ NULL , 0x0L, 0 }
|
|
};
|
|
|
|
typedef struct {
|
|
char *oencbuf; /* Encoded data for sending */
|
|
char *oencptr; /* Pointer to the UNSENT encoded data */
|
|
int oencpos; /* Total length of encoded data */
|
|
char *obuf;
|
|
int lastchar; /* Last character we sent */
|
|
|
|
char *iencbuf;
|
|
char *iencptr;
|
|
bool iencrcv; /* Flag: we are receiving packet now */
|
|
int iencfmt; /* Format of packet we are receiving */
|
|
int idlecnt; /* Counter for received HYDRA_DLE characters */
|
|
char *ibuf; /* Decoded packet */
|
|
int isize; /* Decoded packet size */
|
|
|
|
int timeout_send;
|
|
int timeout_recv;
|
|
int timeout_dead;
|
|
|
|
int send_block_size;
|
|
int send_good_blocks; /* Count sent blocks for current block size */
|
|
int recv_block_size;
|
|
|
|
unsigned long txwindow;
|
|
unsigned long rxwindow;
|
|
|
|
unsigned long options;
|
|
unsigned long rxoptions;
|
|
unsigned long txoptions;
|
|
|
|
long recv_rpos_id;
|
|
long send_rpos_id;
|
|
|
|
bool batchesdone; /* It is not our first batch? */
|
|
|
|
s_hydrapkt *pktqueue;
|
|
int n_pkts;
|
|
} s_hydrainfo;
|
|
|
|
static char *hydra_putlong(char *buf, long val)
|
|
{
|
|
buf[0] = ( ((unsigned long) val) ) & 0xff;
|
|
buf[1] = ( ((unsigned long) val) >> 8 ) & 0xff;
|
|
buf[2] = ( ((unsigned long) val) >> 16 ) & 0xff;
|
|
buf[3] = ( ((unsigned long) val) >> 24 ) & 0xff;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *hydra_putword(char *buf, int val)
|
|
{
|
|
buf[0] = ( ((unsigned int) val) ) & 0xff;
|
|
buf[1] = ( ((unsigned int) val) >> 8 ) & 0xff;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static long hydra_getlong(const char *buf)
|
|
{
|
|
return ( (unsigned long) ((unsigned char) buf[0]) )
|
|
| ( (unsigned long) ((unsigned char) buf[1]) << 8 )
|
|
| ( (unsigned long) ((unsigned char) buf[2]) << 16 )
|
|
| ( (unsigned long) ((unsigned char) buf[3]) << 24 );
|
|
}
|
|
|
|
static int hydra_getword(const char *buf)
|
|
{
|
|
return ( (unsigned int) ((unsigned char) buf[0]) )
|
|
| ( (unsigned int) ((unsigned char) buf[1]) << 8 );
|
|
}
|
|
|
|
static char *hydra_putflags(char *buf, unsigned long flags, size_t szbuf)
|
|
{
|
|
int i;
|
|
size_t szused = 0;
|
|
|
|
*buf = '\0';
|
|
|
|
for( i = 0; hydraflags[i].str; i++ )
|
|
{
|
|
if( hydraflags[i].val & flags )
|
|
{
|
|
if( szused + hydraflags[i].len + 2 > szbuf )
|
|
{
|
|
ASSERT_MSG();
|
|
break;
|
|
}
|
|
else if( szused > 0 )
|
|
{
|
|
*buf++ = ',';
|
|
++szused;
|
|
}
|
|
strcpy(buf, hydraflags[i].str);
|
|
buf += hydraflags[i].len;
|
|
szused += hydraflags[i].len;
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static unsigned long hydra_getflags(char *buf)
|
|
{
|
|
int i;
|
|
char *p;
|
|
unsigned long result = 0L;
|
|
|
|
for( p = strtok(buf, ","); p; p = strtok(NULL, ",") )
|
|
{
|
|
for( i = 0; hydraflags[i].str; i++ )
|
|
{
|
|
if( strcmp(p, hydraflags[i].str) == 0 )
|
|
{
|
|
result |= hydraflags[i].val;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void hydra_pktdebug(bool send, s_hydrainfo *hi, const char *data,
|
|
e_hydra_pkttype pkttype, e_hydra_char format,
|
|
bool crc32, size_t sz)
|
|
{
|
|
char *p;
|
|
|
|
DEB((D_PROT, "hydra_pktdebug: %s packet '%s' #%d (len=%ld, format='%s', crc=%d)",
|
|
send ? "<-" : "->", HYDRA_PKTNAME(pkttype), pkttype, sz,
|
|
HYDRA_CHRNAME(format), crc32 ? 32 : 16));
|
|
|
|
/*
|
|
* For some packet types we will log more information
|
|
*/
|
|
switch(pkttype) {
|
|
case HPKT_FINFO:
|
|
DEB((D_PROT, "hydra_pktdebug: FINFO \"%s\"",
|
|
p = string_printable_buffer(data, sz)));
|
|
if( p ) free(p);
|
|
break;
|
|
case HPKT_FINFOACK:
|
|
if( data[0] == '\0' )
|
|
DEB((D_PROT, "hydra_pktdebug: FINFOACK (End Of Batch)"));
|
|
else
|
|
DEB((D_PROT, "hydra_pktdebug: FINFOACK (offset=%ld)",
|
|
(long)hydra_getlong(data)));
|
|
break;
|
|
case HPKT_DATA:
|
|
DEB((D_PROT, "hydra_pktdebug: DATA (offset=%ld, length=%ld)",
|
|
(long)hydra_getlong(data), (long)(sz - 5)));
|
|
break;
|
|
case HPKT_DATAACK:
|
|
DEB((D_PROT, "hydra_pktdebug: DATAACK (offset=%ld)",
|
|
hydra_getlong(data)));
|
|
break;
|
|
case HPKT_RPOS:
|
|
DEB((D_PROT, "hydra_pktdebug: RPOS (pos=%ld, blklen=%d, syncid=%ld)",
|
|
hydra_getlong(data), hydra_getword(data+4),
|
|
hydra_getlong(data+6)));
|
|
break;
|
|
case HPKT_EOF:
|
|
DEB((D_PROT, "hydra_pktdebug: EOF (offset=%ld)",
|
|
hydra_getlong(data)));
|
|
break;
|
|
default:
|
|
/* Avoid warnings about unhandled values */
|
|
break;
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
static char *hydra_putbinbyte(s_hydrainfo *hi, char *buf, unsigned char c)
|
|
{
|
|
unsigned char n = c;
|
|
|
|
if( hi->txoptions & HOPT_HIGHCTL )
|
|
n &= 0x7f;
|
|
|
|
if( (n == HYDRA_DLE)
|
|
|| ((hi->txoptions & HOPT_XONXOFF) && ((n == XON) || (n == XOFF)))
|
|
|| ((hi->txoptions & HOPT_TELENET) && (n == '\r') && (hi->lastchar == '@'))
|
|
|| ((hi->txoptions & HOPT_CTLCHRS) && ((n < 32) || (n == 127))) )
|
|
{
|
|
*buf++ = HYDRA_DLE;
|
|
c ^= 0x40;
|
|
}
|
|
|
|
*buf++ = c;
|
|
hi->lastchar = n;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *hydra_puthexbyte(char *buf, unsigned char c)
|
|
{
|
|
static const char hexdigit[] = "0123456789abcdef";
|
|
|
|
if( c & 0x80 )
|
|
{
|
|
*buf++ = '\\';
|
|
*buf++ = hexdigit[(c >> 4) & 0x0f];
|
|
*buf++ = hexdigit[(c ) & 0x0f];
|
|
}
|
|
else if( (c < 32) || (c == 127) )
|
|
{
|
|
*buf++ = HYDRA_DLE;
|
|
*buf++ = c ^ 0x40;
|
|
}
|
|
else if( c == '\\' )
|
|
{
|
|
*buf++ = '\\';
|
|
*buf++ = '\\';
|
|
}
|
|
else
|
|
*buf++ = c;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *hydra_putascblock(s_hydrainfo *hi, char *buf, char *src, size_t szsrc)
|
|
{
|
|
int ch = 0;
|
|
int bitshift = 0;
|
|
|
|
for( ; szsrc > 0; --szsrc, ++src )
|
|
{
|
|
ch |= ((unsigned char)(*src) << bitshift);
|
|
|
|
buf = hydra_putbinbyte(hi, buf, ((unsigned char)(ch) & 0x7f));
|
|
ch >>= 7;
|
|
|
|
if( ++bitshift >= 7 )
|
|
{
|
|
buf = hydra_putbinbyte(hi, buf, ((unsigned char)(ch) & 0x7f));
|
|
ch = bitshift = 0;
|
|
}
|
|
}
|
|
|
|
if( bitshift > 0 )
|
|
buf = hydra_putbinbyte(hi, buf, ((unsigned char)(ch) & 0x7f));
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *hydra_putuueblock(char *buf, char *src, size_t szsrc)
|
|
{
|
|
for( ; szsrc >= 3; src += 3, szsrc -= 3 )
|
|
{
|
|
*buf++ = HYDRA_UUENC(src[0] >> 2);
|
|
*buf++ = HYDRA_UUENC(((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0f));
|
|
*buf++ = HYDRA_UUENC(((src[1] << 2) & 0x3c) | ((src[2] >> 6) & 0x03));
|
|
*buf++ = HYDRA_UUENC(src[2] & 0x3f);
|
|
}
|
|
|
|
if( szsrc > 0 )
|
|
{
|
|
*buf++ = HYDRA_UUENC(src[0] >> 2);
|
|
*buf++ = HYDRA_UUENC(((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0f));
|
|
if( szsrc == 2 )
|
|
*buf++ = HYDRA_UUENC((src[1] << 2) & 0x3c);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *hydra_putpkt(s_hydrainfo *hi, char *dst, char *src, size_t szsrc,
|
|
enum hydra_pkttype pkttype)
|
|
{
|
|
enum hydra_char format;
|
|
char *obuf;
|
|
int pos;
|
|
|
|
switch(pkttype) {
|
|
case HPKT_START:
|
|
case HPKT_INIT:
|
|
case HPKT_INITACK:
|
|
case HPKT_END:
|
|
case HPKT_IDLE:
|
|
format = HCHR_HEXPKT;
|
|
break;
|
|
default:
|
|
if( hi->txoptions & HOPT_HIGHBIT )
|
|
{
|
|
if( (hi->txoptions & HOPT_CTLCHRS)
|
|
&& (hi->txoptions & HOPT_CANUUE ) )
|
|
format = HCHR_UUEPKT;
|
|
else if( hi->txoptions & HOPT_CANASC )
|
|
format = HCHR_ASCPKT;
|
|
else
|
|
format = HCHR_HEXPKT;
|
|
}
|
|
else
|
|
format = HCHR_BINPKT;
|
|
} /* end of switch */
|
|
|
|
#ifdef DEBUG
|
|
hydra_pktdebug(TRUE, hi, src, pkttype, format,
|
|
(format != HCHR_HEXPKT && (hi->txoptions & HOPT_CRC32)), szsrc);
|
|
#endif
|
|
|
|
src[szsrc++] = pkttype;
|
|
|
|
if( format != HCHR_HEXPKT && (hi->txoptions & HOPT_CRC32) )
|
|
{
|
|
unsigned long crc32 = ~getcrc32ccitt(src, szsrc);
|
|
|
|
src[szsrc++] = (((unsigned char) (crc32 )) & 0xff);
|
|
src[szsrc++] = (((unsigned char) (crc32 >> 8 )) & 0xff);
|
|
src[szsrc++] = (((unsigned char) (crc32 >> 16)) & 0xff);
|
|
src[szsrc++] = (((unsigned char) (crc32 >> 24)) & 0xff);
|
|
}
|
|
else
|
|
{
|
|
unsigned short crc16 = ~getcrc16ccitt(src, szsrc);
|
|
|
|
src[szsrc++] = (((unsigned char) (crc16 )) & 0xff);
|
|
src[szsrc++] = (((unsigned char) (crc16 >> 8)) & 0xff);
|
|
}
|
|
|
|
obuf = dst;
|
|
*obuf++ = HYDRA_DLE;
|
|
*obuf++ = format;
|
|
|
|
switch(format) {
|
|
case HCHR_BINPKT:
|
|
for( pos = 0; pos < szsrc; pos++ )
|
|
obuf = hydra_putbinbyte(hi, obuf, *src++);
|
|
break;
|
|
case HCHR_HEXPKT:
|
|
for( pos = 0; pos < szsrc; pos++ )
|
|
obuf = hydra_puthexbyte(obuf, *src++);
|
|
break;
|
|
case HCHR_ASCPKT:
|
|
obuf = hydra_putascblock(hi, obuf, src, szsrc);
|
|
break;
|
|
case HCHR_UUEPKT:
|
|
obuf = hydra_putuueblock(obuf, src, szsrc);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
*obuf++ = HYDRA_DLE;
|
|
*obuf++ = HCHR_PKTEND;
|
|
|
|
if( pkttype != HPKT_DATA && format != HCHR_BINPKT )
|
|
{
|
|
*obuf++ = '\r';
|
|
*obuf++ = '\n';
|
|
}
|
|
|
|
return obuf;
|
|
}
|
|
|
|
void hydra_queuepkt(s_hydrainfo *hi, char *data, size_t szdata,
|
|
enum hydra_pkttype pkttype)
|
|
{
|
|
char *endptr = NULL;
|
|
char *buffer = NULL;
|
|
size_t pktlen;
|
|
|
|
ASSERT(szdata <= HYDRA_MAXBLKLEN);
|
|
|
|
if( data && szdata > 0 )
|
|
{
|
|
buffer = xmalloc(HYDRA_BUFLEN);
|
|
endptr = hydra_putpkt(hi, buffer, data, szdata, pkttype);
|
|
pktlen = endptr - buffer;
|
|
|
|
/* Check for buffer overflow */
|
|
ASSERT(pktlen <= HYDRA_BUFLEN);
|
|
|
|
/* Release unused memory */
|
|
buffer = xrealloc(buffer, endptr - buffer);
|
|
}
|
|
else
|
|
{
|
|
buffer = xmalloc(HYDRA_OVERHEAD*3);
|
|
endptr = hydra_putpkt(hi, buffer, hi->obuf, 0, pkttype);
|
|
pktlen = endptr - buffer;
|
|
|
|
/* Check for buffer overflow */
|
|
ASSERT(pktlen <= (HYDRA_OVERHEAD*3));
|
|
}
|
|
|
|
/* Add new entry to the queue */
|
|
hi->pktqueue = xrealloc(hi->pktqueue, sizeof(s_hydrapkt)*(hi->n_pkts + 1));
|
|
memset(&hi->pktqueue[hi->n_pkts], '\0', sizeof(s_hydrapkt));
|
|
|
|
/* Set packet data */
|
|
hi->pktqueue[hi->n_pkts].sent = FALSE;
|
|
hi->pktqueue[hi->n_pkts].type = pkttype;
|
|
hi->pktqueue[hi->n_pkts].data = buffer;
|
|
hi->pktqueue[hi->n_pkts].size = pktlen;
|
|
hi->n_pkts++;
|
|
|
|
DEB((D_PROT, "hydra_queuepkt: queue packet '%s' #%d, %ld bytes",
|
|
HYDRA_PKTNAME(pkttype), (int)pkttype, (long)(endptr - buffer)));
|
|
}
|
|
|
|
static int hydra_buffer_packet(s_hydrainfo *hi, s_hydrapkt *pkt)
|
|
{
|
|
/* Is there space for the new pkt? */
|
|
if( hi->oencpos + pkt->size > HYDRA_BUFLEN ) return 1;
|
|
|
|
if( pkt->data )
|
|
{
|
|
DEB((D_PROT, "hydra_buffer_packet: buffer packet '%s' #%d, %ld bytes",
|
|
HYDRA_PKTNAME(pkt->type), (int)pkt->type, (long)pkt->size));
|
|
memcpy(hi->oencbuf + hi->oencpos, pkt->data, pkt->size);
|
|
hi->oencpos += pkt->size;
|
|
free(pkt->data);
|
|
pkt->data = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hydra_send(s_hydrainfo *hi)
|
|
{
|
|
int i;
|
|
|
|
if( hi->oencpos == 0 && hi->pktqueue )
|
|
{
|
|
/*
|
|
* Buffer is empty and there are unsent packets
|
|
*/
|
|
for( i = 0; i < hi->n_pkts; i++ )
|
|
{
|
|
if( hi->pktqueue[i].sent == FALSE )
|
|
{
|
|
if( hydra_buffer_packet(hi, &hi->pktqueue[i]) )
|
|
break;
|
|
else
|
|
hi->pktqueue[i].sent = TRUE;
|
|
}
|
|
}
|
|
/* If the packet queue is empty, free it */
|
|
if( i >= hi->n_pkts )
|
|
{
|
|
if( hi->pktqueue )
|
|
{
|
|
free(hi->pktqueue);
|
|
hi->pktqueue = NULL;
|
|
}
|
|
hi->n_pkts = 0;
|
|
}
|
|
}
|
|
|
|
/* Send buffered data */
|
|
if( hi->oencpos )
|
|
{
|
|
int n = tty_write(hi->oencptr, hi->oencpos);
|
|
|
|
if( n >= hi->oencpos )
|
|
{
|
|
hi->oencptr = hi->oencbuf;
|
|
hi->oencpos = 0;
|
|
}
|
|
else if( n > 0 )
|
|
{
|
|
hi->oencptr += n;
|
|
hi->oencpos -= n;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char *hydra_getbinblock(char *dst, size_t szdst, char *src, size_t szsrc)
|
|
{
|
|
if( szsrc > szdst )
|
|
{
|
|
log("Hydra: BIN packet buffer overflow detected");
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(dst, src, szsrc);
|
|
|
|
return dst + szsrc;
|
|
}
|
|
|
|
static char *hydra_gethexblock(char *dst, size_t szdst, char *src, size_t szsrc)
|
|
{
|
|
char *in = src;
|
|
char *out = dst;
|
|
char *enddst = dst + szdst - 1;
|
|
char *endsrc = src + szsrc - 1;
|
|
int hex1 = 0;
|
|
int hex2 = 0;
|
|
|
|
while( in <= endsrc && out <= enddst )
|
|
{
|
|
if( (*in == '\\') && (*++in != '\\') )
|
|
{
|
|
hex1 = (unsigned char)(*in++);
|
|
hex2 = (unsigned char)(*in++);
|
|
|
|
if( (hex1 -= '0') > 9 )
|
|
hex1 -= ('a' - ':');
|
|
if( (hex2 -= '0') > 9 )
|
|
hex2 -= ('a' - ':');
|
|
|
|
*out++ = ((hex1 << 4) | hex2) & 0xff;
|
|
}
|
|
else
|
|
*out++ = *in++;
|
|
}
|
|
|
|
if( out > enddst )
|
|
log("Hydra: HEX decoder buffer overflow detected");
|
|
|
|
return out;
|
|
}
|
|
|
|
static char *hydra_getascblock(char *dst, size_t szdst, char *src, size_t szsrc)
|
|
{
|
|
char *in = src;
|
|
char *out = dst;
|
|
char *enddst = dst + szdst - 1;
|
|
char *endsrc = src + szsrc - 1;
|
|
int n = 0;
|
|
int c = 0;
|
|
|
|
while( (in <= endsrc) && (out <= enddst) )
|
|
{
|
|
c |= (((unsigned char)(*in) & 0x7f) << n);
|
|
|
|
if( (n += 7) >= 8 )
|
|
{
|
|
*out++ = (unsigned char)(c & 0xff);
|
|
c >>= 8;
|
|
n -= 8;
|
|
}
|
|
++in;
|
|
}
|
|
|
|
if( out > enddst )
|
|
log("Hydra: ASC decoder buffer overflow detected");
|
|
|
|
return out;
|
|
}
|
|
|
|
static char *hydra_getuueblock(char *dst, size_t szdst, char *src, size_t szsrc)
|
|
{
|
|
char *in = src;
|
|
char *out = dst;
|
|
|
|
/*
|
|
* Check UUE block size
|
|
*/
|
|
if( (szsrc % 4) == 1 )
|
|
{
|
|
log("Hydra: not enough characters in UUE block");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Check destination buffer size to avoid overflows
|
|
*/
|
|
if( ((szsrc / 4) * 4 + (szsrc % 4)) > szdst )
|
|
{
|
|
log("Hydra: buffer overflow in UU decoder");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* UU decoder
|
|
*/
|
|
while( szsrc >= 4 )
|
|
{
|
|
if( !HYDRA_ISUUE(in[0]) || !HYDRA_ISUUE(in[1])
|
|
|| !HYDRA_ISUUE(in[2]) || !HYDRA_ISUUE(in[3]) )
|
|
{
|
|
log("Hydra: character out of range in UUE block");
|
|
return NULL;
|
|
}
|
|
|
|
*out++ = (unsigned char)((HYDRA_UUDEC(in[0]) << 2)
|
|
| (HYDRA_UUDEC(in[1]) >> 4));
|
|
*out++ = (unsigned char)((HYDRA_UUDEC(in[1]) << 4)
|
|
| (HYDRA_UUDEC(in[2]) >> 2));
|
|
*out++ = (unsigned char)((HYDRA_UUDEC(in[2]) << 6)
|
|
| (HYDRA_UUDEC(in[3]) ));
|
|
|
|
in += 4;
|
|
szsrc -= 4;
|
|
}
|
|
|
|
if( szsrc >= 2 )
|
|
{
|
|
if( !HYDRA_ISUUE(in[0]) || !HYDRA_ISUUE(in[1]) )
|
|
{
|
|
log("Hydra: character out of range in UUE block");
|
|
return NULL;
|
|
}
|
|
*out++ = (unsigned char)((HYDRA_UUDEC(in[0]) << 2)
|
|
| (HYDRA_UUDEC(in[1]) >> 4));
|
|
|
|
if( szsrc == 3 )
|
|
{
|
|
if( !HYDRA_ISUUE(in[2]) )
|
|
{
|
|
log("Hydra: character out of range in UUE block");
|
|
return NULL;
|
|
}
|
|
*out++ = (unsigned char)((HYDRA_UUDEC(in[1]) << 4)
|
|
| (HYDRA_UUDEC(in[2]) >> 2));
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static int hydra_recv(s_hydrainfo *hi)
|
|
{
|
|
int n, c;
|
|
char *p;
|
|
bool goodcrc = FALSE;
|
|
int pkttype;
|
|
|
|
while( (c = GETCHAR(0)) >= 0 )
|
|
{
|
|
if( hi->rxoptions & HOPT_HIGHBIT )
|
|
c &= 0x7f;
|
|
|
|
n = c;
|
|
if( hi->rxoptions & HOPT_HIGHCTL )
|
|
n &= 0x7f;
|
|
|
|
if( (n != HYDRA_DLE)
|
|
&& (((hi->rxoptions & HOPT_XONXOFF) && ((n == XON) || (n == XOFF)))
|
|
|| ((hi->rxoptions & HOPT_CTLCHRS) && ((n < 32 ) || (n == 127 )))) )
|
|
{
|
|
DEB((D_PROT, "hydra_recv: received character 0x%02x discarded", n));
|
|
continue;
|
|
}
|
|
|
|
if( c == HYDRA_DLE )
|
|
{
|
|
if( ++hi->idlecnt >= 5 )
|
|
{
|
|
log("Hydra: got 5 DLE characters (abort)");
|
|
return HPKT_EXIT;
|
|
}
|
|
}
|
|
else if( hi->idlecnt )
|
|
{
|
|
hi->idlecnt = 0;
|
|
|
|
switch(c) {
|
|
case HCHR_PKTEND:
|
|
if( hi->iencrcv )
|
|
{
|
|
size_t encsize = hi->iencptr - hi->iencbuf;
|
|
|
|
switch(hi->iencfmt) {
|
|
case HCHR_BINPKT:
|
|
p = hydra_getbinblock(hi->ibuf, HYDRA_MAXDATALEN, hi->iencbuf, encsize);
|
|
break;
|
|
case HCHR_HEXPKT:
|
|
p = hydra_gethexblock(hi->ibuf, HYDRA_MAXDATALEN, hi->iencbuf, encsize);
|
|
break;
|
|
case HCHR_ASCPKT:
|
|
p = hydra_getascblock(hi->ibuf, HYDRA_MAXDATALEN, hi->iencbuf, encsize);
|
|
break;
|
|
case HCHR_UUEPKT:
|
|
p = hydra_getuueblock(hi->ibuf, HYDRA_MAXDATALEN, hi->iencbuf, encsize);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
hi->iencrcv = FALSE;
|
|
hi->iencptr = hi->iencbuf;
|
|
|
|
if( !p )
|
|
{
|
|
log("Hydra: error decoding packet format %d",
|
|
hi->iencfmt);
|
|
return HPKT_NONE;
|
|
}
|
|
|
|
/*
|
|
* Calculate decoded packet size
|
|
*/
|
|
hi->isize = p - hi->ibuf;
|
|
|
|
if( (hi->iencfmt != HCHR_HEXPKT) && (hi->rxoptions & HOPT_CRC32) )
|
|
{
|
|
goodcrc = CRC32TEST(getcrc32ccitt(hi->ibuf, hi->isize));
|
|
hi->isize -= 4; /* remove CRC-32 */
|
|
}
|
|
else
|
|
{
|
|
goodcrc = CRC16TEST(getcrc16ccitt(hi->ibuf, hi->isize));
|
|
hi->isize -= 2; /* remove CRC-16 */
|
|
}
|
|
|
|
--hi->isize; /* remove packet type */
|
|
|
|
if( hi->isize >= 0 )
|
|
{
|
|
pkttype = hi->ibuf[hi->isize];
|
|
|
|
#ifdef DEBUG
|
|
hydra_pktdebug(FALSE, hi, hi->ibuf, pkttype, hi->iencfmt,
|
|
(hi->iencfmt != HCHR_HEXPKT && (hi->rxoptions & HOPT_CRC32)),
|
|
hi->isize);
|
|
#endif
|
|
|
|
if( !goodcrc )
|
|
log("Hydra: CRC test failed");
|
|
else if( pkttype < HPKT_MIN || pkttype > HPKT_MAX )
|
|
log("Hydra: packet type %d out of range",
|
|
pkttype);
|
|
else
|
|
return pkttype;
|
|
} else
|
|
log("Hydra: got invalid packet (ignored)");
|
|
} else
|
|
log("Hydra: got unexpected packet end");
|
|
|
|
DEB((D_PROT, "hydra_recv: broken packet"));
|
|
|
|
/* Return junk error */
|
|
return HPKT_JUNK;
|
|
|
|
case HCHR_BINPKT:
|
|
case HCHR_HEXPKT:
|
|
case HCHR_ASCPKT:
|
|
case HCHR_UUEPKT:
|
|
if( hi->iencrcv )
|
|
log("Hydra: packet was not received completly");
|
|
|
|
hi->iencrcv = TRUE;
|
|
hi->iencptr = hi->iencbuf;
|
|
hi->iencfmt = c;
|
|
|
|
DEB((D_PROT, "hydra_recv: start receiving '%s' (%d) packet",
|
|
HYDRA_CHRNAME(hi->iencfmt), hi->iencfmt));
|
|
break;
|
|
|
|
default:
|
|
if( hi->iencrcv )
|
|
{
|
|
if( hi->iencptr - hi->iencbuf < HYDRA_BUFLEN )
|
|
*hi->iencptr++ = c ^ 0x40;
|
|
else
|
|
{
|
|
log("Hydra: packet too long");
|
|
hi->iencrcv = FALSE;
|
|
hi->iencptr = hi->iencbuf;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
DEB((D_PROT, "hydra_recv: ignore 0x%02x (escaped)", c ^ 0x40));
|
|
#endif
|
|
}
|
|
}
|
|
else if( hi->iencrcv )
|
|
{
|
|
if( hi->iencptr - hi->iencbuf < HYDRA_BUFLEN )
|
|
*hi->iencptr++ = c;
|
|
else
|
|
{
|
|
log("Hydra: packet too long");
|
|
hi->iencrcv = FALSE;
|
|
hi->iencptr = hi->iencbuf;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
DEB((D_PROT, "hydra_recv: ignore 0x%02x", c));
|
|
#endif
|
|
}
|
|
|
|
return (c == TTY_TIMEOUT) ? HPKT_NONE : HPKT_EXIT;
|
|
}
|
|
|
|
/*
|
|
* Parse received INIT packet. Return -1 value if got unparsable/invalid
|
|
* INIT packet. Probably we should abort transfer in case of such error.
|
|
*/
|
|
static int hydra_parse_init(s_hydrainfo *hi, char *pkt, size_t pktlen)
|
|
{
|
|
char *appinf; /* Application info */
|
|
char *canopt; /* Supported options */
|
|
char *desopt; /* Desired options */
|
|
char *window; /* Tx/Rx window sizes */
|
|
char *prefix; /* Prefix string */
|
|
char *endptr = pkt + pktlen;
|
|
time_t revtime;
|
|
|
|
if( pktlen > 8 && pkt[pktlen-1] == '\0' )
|
|
{
|
|
/*
|
|
* Get other's Hydra revision time stamp
|
|
*/
|
|
sscanf(pkt, "%08lx", &revtime);
|
|
|
|
/*
|
|
* Get other's application info
|
|
*/
|
|
appinf = pkt + 8;
|
|
|
|
/*
|
|
* Get other's supported options, desired options,
|
|
* rx/tx windows sizes and packet prefix string
|
|
*/
|
|
canopt = (appinf && appinf < endptr) ? appinf + strlen(appinf) + 1 : NULL;
|
|
desopt = (canopt && canopt < endptr) ? canopt + strlen(canopt) + 1 : NULL;
|
|
window = (desopt && desopt < endptr) ? desopt + strlen(desopt) + 1 : NULL;
|
|
prefix = (window && window < endptr) ? window + strlen(window) + 1 : NULL;
|
|
|
|
if( appinf && canopt && desopt && window && prefix )
|
|
{
|
|
char buf[256];
|
|
long txwindow = 0L;
|
|
long rxwindow = 0L;
|
|
|
|
DEB((D_PROT, "hydra: revtime = \"%s\"", time_string_long(buf, sizeof(buf), revtime)));
|
|
DEB((D_PROT, "hydra: appinf = \"%s\"", appinf));
|
|
DEB((D_PROT, "hydra: canopt = \"%s\"", canopt));
|
|
DEB((D_PROT, "hydra: desopt = \"%s\"", desopt));
|
|
DEB((D_PROT, "hydra: window = \"%s\"", window));
|
|
DEB((D_PROT, "hydra: prefix = \"%s\"", prefix));
|
|
|
|
hi->rxoptions = hi->options;
|
|
hi->rxoptions |= hydra_getflags(desopt);
|
|
hi->rxoptions &= hydra_getflags(canopt);
|
|
hi->rxoptions &= HYDRA_CANOPT;
|
|
hi->txoptions = hi->rxoptions;
|
|
|
|
if( hi->rxoptions < (hi->options & HYDRA_NECOPT) )
|
|
{
|
|
log("Hydra is incompartible on this link");
|
|
return -1;
|
|
}
|
|
|
|
sscanf(window, "%08lx%08lx", &txwindow, &rxwindow);
|
|
|
|
if( txwindow < 0 ) txwindow = 0L;
|
|
if( rxwindow < 0 ) rxwindow = 0L;
|
|
|
|
/*
|
|
* Log other's hydra information only at 1st batch
|
|
*/
|
|
if( !hi->batchesdone )
|
|
{
|
|
if( hi->rxoptions )
|
|
{
|
|
hydra_putflags(buf, hi->rxoptions, sizeof(buf));
|
|
log("Hydra init \"%s\", \"%s\"",
|
|
string_printable(appinf), buf);
|
|
}
|
|
else
|
|
{
|
|
log("Hydra init \"%s\"",
|
|
string_printable(appinf));
|
|
}
|
|
hi->batchesdone = TRUE;
|
|
}
|
|
|
|
if( (hi->txwindow != txwindow)
|
|
|| (hi->rxwindow != rxwindow) )
|
|
{
|
|
log("Hydra Tx/Rx windows = %ld/%ld bytes",
|
|
txwindow, rxwindow);
|
|
hi->txwindow = txwindow;
|
|
hi->rxwindow = rxwindow;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
log("Hydra got invalid INIT packet");
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void hydra_align_block_size(int *block_size)
|
|
{
|
|
int n = *block_size;
|
|
|
|
if( n <= 64 )
|
|
n = 64;
|
|
else if( n <= 128 )
|
|
n = 128;
|
|
else if( n <= 256 )
|
|
n = 256;
|
|
else if( n <= 512 )
|
|
n = 512;
|
|
else if( n <= 1024 )
|
|
n = 1024;
|
|
else
|
|
n = 2048;
|
|
|
|
*block_size = n;
|
|
}
|
|
|
|
static void hydra_init(s_hydrainfo *hi)
|
|
{
|
|
memset(hi, '\0', sizeof(s_hydrainfo));
|
|
|
|
/*
|
|
* RPOS ID sequence starting value
|
|
*/
|
|
hi->recv_rpos_id = 100;
|
|
hi->send_rpos_id = -1;
|
|
|
|
/*
|
|
* Set rx/tx block sizes
|
|
*/
|
|
hi->send_block_size = 1024;
|
|
hi->recv_block_size = 1024;
|
|
|
|
/*
|
|
* Set timeout values
|
|
*/
|
|
hi->timeout_send = 30;
|
|
hi->timeout_recv = 30;
|
|
hi->timeout_dead = 120;
|
|
|
|
/*
|
|
* Set start up link options
|
|
*/
|
|
hi->options = HYDRA_DEFOPT & HYDRA_CANOPT;
|
|
hi->txoptions = HYDRA_TXIOPT;
|
|
hi->rxoptions = HYDRA_RXIOPT;
|
|
|
|
hi->lastchar = -1;
|
|
hi->oencbuf = xmalloc(HYDRA_BUFLEN);
|
|
hi->oencptr = hi->oencbuf;
|
|
hi->iencbuf = xmalloc(HYDRA_BUFLEN);
|
|
hi->iencptr = hi->iencbuf;
|
|
hi->obuf = xmalloc(HYDRA_MAXDATALEN);
|
|
hi->ibuf = xmalloc(HYDRA_MAXDATALEN);
|
|
}
|
|
|
|
static void hydra_deinit(s_hydrainfo *hi)
|
|
{
|
|
if( hi->iencbuf ) free(hi->iencbuf);
|
|
if( hi->oencbuf ) free(hi->oencbuf);
|
|
if( hi->ibuf ) free(hi->ibuf);
|
|
if( hi->obuf ) free(hi->obuf);
|
|
|
|
memset(hi, '\0', sizeof(s_hydrainfo));
|
|
}
|
|
|
|
int hydra_batch(s_hydrainfo *hi, s_protinfo *pi)
|
|
{
|
|
int rc = PRC_NOERROR;
|
|
int n;
|
|
char *p;
|
|
bool send_EOB = FALSE;
|
|
int send_rc = 0;
|
|
int recv_rc = 0;
|
|
bool send_ready = FALSE;
|
|
bool recv_ready = FALSE;
|
|
enum hydra_txstate txstate = HTX_START;
|
|
enum hydra_rxstate rxstate = HRX_INIT;
|
|
int txtries = 0;
|
|
int rxtries = 0;
|
|
long txlastack = 0L;
|
|
long rxlastack = 0L;
|
|
time_t idletimer = 0;
|
|
time_t sendtimer = 0;
|
|
time_t recvtimer = 0;
|
|
time_t deadtimer = 0;
|
|
#ifdef DEBUG
|
|
int prev_txstate = -1;
|
|
int prev_rxstate = -1;
|
|
#endif
|
|
long last_datapos = -1; /* The last received DATA block offset */
|
|
|
|
timer_set(&deadtimer, hi->timeout_dead);
|
|
|
|
while( txstate != HTX_DONE || rxstate != HRX_DONE )
|
|
{
|
|
#ifdef DEBUG
|
|
if( txstate != prev_txstate || rxstate != prev_rxstate )
|
|
{
|
|
prev_txstate = txstate;
|
|
prev_rxstate = rxstate;
|
|
DEB((D_PROT, "hydra: txstate = '%s' (%d), rxstate = '%s' (%d)",
|
|
HYDRA_TXSTATE(txstate), txstate,
|
|
HYDRA_RXSTATE(rxstate), rxstate));
|
|
}
|
|
#endif
|
|
|
|
if( timer_running(deadtimer) && timer_expired(deadtimer) )
|
|
{
|
|
log("remote dead (timer expired)");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
|
|
if( timer_running(sendtimer) && timer_expired(sendtimer) )
|
|
{
|
|
sendtimer = 0;
|
|
switch(txstate) {
|
|
case HTX_SWAIT: txstate = HTX_START; break;
|
|
case HTX_INITACK: txstate = HTX_INIT; break;
|
|
case HTX_FINFOACK: txstate = HTX_FINFO; break;
|
|
case HTX_DATAACK: txstate = HTX_DATA; break; /* Incorrect! */
|
|
case HTX_EOFACK: txstate = HTX_EOF; break;
|
|
case HTX_ENDACK: txstate = HTX_END; break;
|
|
default:
|
|
log("sendtimer expired, but txstate = %s #%d",
|
|
HYDRA_TXSTATE(txstate), txstate);
|
|
}
|
|
DEB((D_PROT, "hydra: send timer expired, new txstate = '%s' #%d",
|
|
HYDRA_TXSTATE(txstate), txstate));
|
|
}
|
|
|
|
if( timer_running(recvtimer) && timer_expired(recvtimer) )
|
|
{
|
|
recvtimer = 0;
|
|
switch(rxstate) {
|
|
case HRX_RPosAck:
|
|
/* Decrease data block size */
|
|
hi->recv_block_size >>= 1;
|
|
hydra_align_block_size(&hi->recv_block_size);
|
|
|
|
/* Send RPOS packet again */
|
|
rxstate = HRX_RPOS;
|
|
break;
|
|
|
|
case HRX_SkipAck: rxstate = HRX_Skip; break;
|
|
case HRX_DATA: rxstate = HRX_RPOS; break;
|
|
default:
|
|
log("recvtimer expired, but rxstate = %s #%d",
|
|
HYDRA_RXSTATE(rxstate), rxstate);
|
|
}
|
|
DEB((D_PROT, "hydra: recv timer expired, new rxstate = '%s' #%d",
|
|
HYDRA_RXSTATE(rxstate), rxstate));
|
|
}
|
|
|
|
switch(txstate) {
|
|
case HTX_START:
|
|
if( ++txtries < 10 )
|
|
{
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
PUTSTR("hydra\r");
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_START);
|
|
txstate = HTX_SWAIT;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra unable to initiate session");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
case HTX_INIT:
|
|
if( ++txtries < 10 )
|
|
{
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
|
|
p = hi->obuf;
|
|
|
|
/* Application info */
|
|
p += sprintf(p, "%08lx%s,%s", (long)0L, BF_NAME, BF_VERSION) + 1;
|
|
|
|
/* What we can */
|
|
p = hydra_putflags(p, HYDRA_CANOPT, 256) + 1;
|
|
|
|
/* What we want */
|
|
p = hydra_putflags(p, HYDRA_DEFOPT, 256) + 1;
|
|
|
|
/* Tx/Rx window sizes */
|
|
p += sprintf(p, "%08lx%08lx", (long)0L, (long)0L) + 1;
|
|
|
|
/* Pkt prefix string we want */
|
|
*p++ = '\0';
|
|
|
|
hydra_queuepkt(hi, hi->obuf, p - hi->obuf, HPKT_INIT);
|
|
txstate = HTX_INITACK;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra too many tries sending INIT packet");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
case HTX_RINIT:
|
|
if( rxstate != HRX_INIT )
|
|
txstate = HTX_NextFile;
|
|
break;
|
|
|
|
case HTX_NextFile:
|
|
if( pi->send && pi->send->fp )
|
|
p_tx_fclose(pi);
|
|
|
|
send_EOB = p_tx_fopen(pi) ? TRUE : FALSE;
|
|
|
|
txtries = 0;
|
|
txstate = HTX_FINFO;
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case HTX_FINFO:
|
|
if( ++txtries < 10 )
|
|
{
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
|
|
if( send_EOB == FALSE )
|
|
{
|
|
char dosname[13];
|
|
|
|
n = sprintf(hi->obuf, "%08lx%08lx%08lx%08lx%08lx",
|
|
(long)pi->send->mod_time,
|
|
(long)pi->send->bytes_total,
|
|
(long)0L, (long)0L, (long)0L);
|
|
|
|
file_get_dos_name(dosname, pi->send->net_name);
|
|
strnxcpy(hi->obuf + n, dosname, BFORCE_MAX_NAME);
|
|
n += strlen(hi->obuf + n) + 1;
|
|
|
|
if( strcmp(dosname, pi->send->net_name) )
|
|
{
|
|
strnxcpy(hi->obuf + n, pi->send->net_name, BFORCE_MAX_NAME);
|
|
n += strlen(hi->obuf + n) + 1;
|
|
}
|
|
}
|
|
else /* Send 'EOB' */
|
|
{
|
|
n = 1;
|
|
hi->obuf[0] = '\0';
|
|
}
|
|
|
|
hydra_queuepkt(hi, hi->obuf, n, HPKT_FINFO);
|
|
txstate = HTX_FINFOACK;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra too many tries sending FINFO packet");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
case HTX_DATA:
|
|
if( pi->send->eofseen )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
else if( hi->oencpos == 0 && hi->n_pkts == 0 )
|
|
{
|
|
sendtimer = 0;
|
|
|
|
n = p_tx_readfile(hi->obuf+4, hi->send_block_size, pi);
|
|
if( n > 0 )
|
|
{
|
|
timer_set(&deadtimer, hi->timeout_dead);
|
|
|
|
hydra_putlong(hi->obuf, (long)pi->send->bytes_sent);
|
|
pi->send->bytes_sent += n;
|
|
|
|
p = hydra_putpkt(hi, hi->oencbuf, hi->obuf, n+4, HPKT_DATA);
|
|
hi->oencpos = p - hi->oencbuf;
|
|
|
|
/* Increase number of good blocks */
|
|
++hi->send_good_blocks;
|
|
|
|
/* Is it a good time for raising block size? */
|
|
if( hi->send_block_size < 2048 )
|
|
{
|
|
if( (hi->send_good_blocks * hi->send_block_size) >= 4096 )
|
|
{
|
|
hi->send_good_blocks = 0;
|
|
hi->send_block_size <<= 1;
|
|
hydra_align_block_size(&hi->send_block_size);
|
|
DEB((D_PROT, "hydra: Tx now use %ld bytes blocks",
|
|
(long)hi->send_block_size));
|
|
}
|
|
}
|
|
|
|
if( (hi->txwindow > 0)
|
|
&& (pi->send->bytes_sent >= txlastack + hi->txwindow) )
|
|
{
|
|
/*
|
|
* Tx window reached its maximal size,
|
|
* continue sending only after receiving
|
|
* of DATAACK packet with correct offset
|
|
*/
|
|
if( txtries > 0 )
|
|
timer_set(&sendtimer, hi->timeout_send/2);
|
|
else
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
|
|
txstate = HTX_DATAACK;
|
|
}
|
|
else if( pi->send->eofseen )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HTX_EOF:
|
|
if( ++txtries < 10 )
|
|
{
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
|
|
if( pi->send )
|
|
{
|
|
if( pi->send->status == FSTAT_SKIPPED )
|
|
hydra_putlong(hi->obuf, (long)-1L);
|
|
else if( pi->send->status == FSTAT_REFUSED )
|
|
hydra_putlong(hi->obuf, (long)-2L);
|
|
else if( pi->send->eofseen )
|
|
hydra_putlong(hi->obuf, (long)pi->send->bytes_sent);
|
|
else
|
|
hydra_putlong(hi->obuf, (long)-2L);
|
|
}
|
|
else
|
|
hydra_putlong(hi->obuf, (long)-2L);
|
|
|
|
hydra_queuepkt(hi, hi->obuf, 4, HPKT_EOF);
|
|
txstate = HTX_EOFACK;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra too many tries sending EOF packet");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
case HTX_REND:
|
|
if( rxstate == HRX_DONE )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_END;
|
|
}
|
|
else if( !timer_running(idletimer) || timer_expired(idletimer) )
|
|
{
|
|
timer_set(&idletimer, 20);
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_IDLE);
|
|
}
|
|
break;
|
|
|
|
case HTX_END:
|
|
if( ++txtries < 10 )
|
|
{
|
|
timer_set(&sendtimer, hi->timeout_send);
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_END);
|
|
txstate = HTX_ENDACK;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra too many tries sending END packet");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Do nothing, but avoid warning message */
|
|
break;
|
|
}
|
|
|
|
switch(rxstate) {
|
|
case HRX_RPOS:
|
|
case HRX_Skip:
|
|
if( ++rxtries < 10 )
|
|
{
|
|
long pos;
|
|
|
|
timer_set(&recvtimer, hi->timeout_recv);
|
|
|
|
if( rxstate == HRX_Skip )
|
|
pos = (pi->recv->status == FSTAT_SKIPPED) ? -1L : -2L;
|
|
else
|
|
pos = pi->recv->bytes_received;
|
|
|
|
hydra_putlong(hi->obuf, pos);
|
|
hydra_putword(hi->obuf + 4, hi->recv_block_size);
|
|
hydra_putlong(hi->obuf + 6, hi->recv_rpos_id);
|
|
hydra_queuepkt(hi, hi->obuf, 10, HPKT_RPOS);
|
|
|
|
rxstate = (rxstate == HRX_Skip) ? HRX_SkipAck : HRX_RPosAck;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra too many tries sending RPOS packet");
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Do nothing, but avoid warning message */
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if( txstate != prev_txstate || rxstate != prev_rxstate )
|
|
{
|
|
prev_txstate = txstate;
|
|
prev_rxstate = rxstate;
|
|
DEB((D_PROT, "hydra: txstate = '%s' (%d), rxstate = '%s' (%d)",
|
|
HYDRA_TXSTATE(txstate), txstate,
|
|
HYDRA_RXSTATE(rxstate), rxstate));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check current CPS, session time limits, etc.
|
|
*/
|
|
if( (rc = p_info(pi, 1)) ) gotoexit(rc);
|
|
|
|
/*
|
|
* Send/receive as much data as possible, but without delays
|
|
*/
|
|
send_ready = recv_ready = FALSE;
|
|
|
|
if( hi->oencpos > 0 || hi->n_pkts )
|
|
n = tty_xselect(&recv_ready, &send_ready, 10);
|
|
else
|
|
n = tty_xselect(&recv_ready, NULL, 10);
|
|
|
|
if( n < 0 && n != TTY_TIMEOUT )
|
|
gotoexit(PRC_ERROR);
|
|
|
|
recv_rc = HPKT_NONE;
|
|
send_rc = 0;
|
|
|
|
if( recv_ready && (recv_rc = hydra_recv(hi)) == HPKT_EXIT )
|
|
gotoexit(PRC_ERROR);
|
|
if( send_ready && (send_rc = hydra_send(hi)) < 0 )
|
|
gotoexit(PRC_ERROR);
|
|
|
|
switch(recv_rc) {
|
|
case HPKT_NONE:
|
|
break;
|
|
|
|
case HPKT_JUNK:
|
|
if( rxstate == HRX_DATA )
|
|
{
|
|
/* Increase RPOS packet ID number */
|
|
hi->recv_rpos_id++;
|
|
|
|
/* Decrease data block size */
|
|
hi->recv_block_size >>= 1;
|
|
hydra_align_block_size(&hi->recv_block_size);
|
|
|
|
rxtries = 0;
|
|
rxstate = HRX_RPOS;
|
|
}
|
|
break;
|
|
|
|
case HPKT_START:
|
|
if( txstate == HTX_START || txstate == HTX_SWAIT )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_INIT;
|
|
}
|
|
break;
|
|
|
|
case HPKT_INIT:
|
|
if( rxstate == HRX_INIT )
|
|
{
|
|
if( hydra_parse_init(hi, hi->ibuf, hi->isize) )
|
|
{
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
rxtries = 0;
|
|
rxstate = HRX_FINFO;
|
|
}
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_INITACK);
|
|
break;
|
|
|
|
case HPKT_INITACK:
|
|
if( txstate == HTX_INIT || txstate == HTX_INITACK )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_RINIT;
|
|
}
|
|
break;
|
|
|
|
case HPKT_FINFO:
|
|
if( rxstate == HRX_FINFO )
|
|
{
|
|
/* Check for EOB FINFO packet */
|
|
if( hi->ibuf[0] == '\0' )
|
|
{
|
|
hydra_putlong(hi->obuf, 0L);
|
|
hydra_queuepkt(hi, hi->obuf, 4, HPKT_FINFOACK);
|
|
rxstate = HRX_DONE;
|
|
}
|
|
else if( hi->isize > 41 && hi->ibuf[hi->isize-1] == '\0' )
|
|
{
|
|
time_t modtime;
|
|
size_t filesize;
|
|
char *filename;
|
|
char *p;
|
|
|
|
/* Get file modification time and size */
|
|
sscanf(hi->ibuf, "%08lx%08x%*08x%*08x%*08x",
|
|
&modtime, &filesize);
|
|
|
|
/* Convert local time -> UTC */
|
|
modtime = localtogmt(modtime);
|
|
|
|
/* Select short file name */
|
|
filename = p = hi->ibuf + 40;
|
|
while( *p && p < (hi->ibuf + hi->isize) )
|
|
++p;
|
|
|
|
if( p == filename )
|
|
{
|
|
/* Got FINFO without file name */
|
|
filename = "bad_name";
|
|
}
|
|
else if( *p )
|
|
{
|
|
/* Got not null-terminated file name */
|
|
*(++p) = '\0';
|
|
}
|
|
else
|
|
{
|
|
/* Try long file name */
|
|
char *long_filename = ++p;
|
|
|
|
while( *p && p < (hi->ibuf + hi->isize) )
|
|
++p;
|
|
|
|
/* Accept only null-terminated long file names */
|
|
if( *p == '\0' && p > long_filename )
|
|
filename = long_filename;
|
|
}
|
|
|
|
switch( p_rx_fopen(pi, filename, filesize, modtime, 0) ) {
|
|
case 0: /* No errors */
|
|
rxlastack = (long)pi->recv->bytes_skipped;
|
|
last_datapos = -1;
|
|
hydra_putlong(hi->obuf, (long)pi->recv->bytes_skipped);
|
|
timer_set(&recvtimer, hi->timeout_recv);
|
|
rxstate = HRX_DATA;
|
|
break;
|
|
|
|
case 1: /* SKIP (non-destructive) */
|
|
hydra_putlong(hi->obuf, -2L);
|
|
break;
|
|
|
|
case 2: /* SKIP (destructive) */
|
|
hydra_putlong(hi->obuf, -1L);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
hydra_queuepkt(hi, hi->obuf, 4, HPKT_FINFOACK);
|
|
}
|
|
else
|
|
log("Hydra: got invalid FINFO packet (ignored)");
|
|
}
|
|
else if( rxstate == HRX_DONE )
|
|
{
|
|
hydra_putlong(hi->obuf, -2);
|
|
hydra_queuepkt(hi, hi->obuf, 4, HPKT_FINFOACK);
|
|
}
|
|
else
|
|
log("Hydra: unexpected FINFO packet (ignored)");
|
|
break;
|
|
|
|
case HPKT_FINFOACK:
|
|
if( txstate == HTX_FINFO || txstate == HTX_FINFOACK )
|
|
{
|
|
long offs = 0L;
|
|
|
|
if( send_EOB == TRUE )
|
|
{
|
|
timer_set(&idletimer, 20);
|
|
sendtimer = 0;
|
|
txtries = 0;
|
|
txstate = HTX_REND;
|
|
}
|
|
else if( hi->isize < 4 )
|
|
{
|
|
log("Hydra: got invalid FINFOACK packet (ignored)");
|
|
}
|
|
else if( (offs = hydra_getlong(hi->ibuf)) == 0 )
|
|
{
|
|
txlastack = 0;
|
|
txtries = 0;
|
|
txstate = HTX_DATA;
|
|
}
|
|
else if( offs > 0 )
|
|
{
|
|
if( fseek(pi->send->fp, offs, SEEK_SET) == 0 )
|
|
{
|
|
txlastack = offs;
|
|
pi->send->bytes_skipped = pi->send->bytes_sent = offs;
|
|
log("send file from offset %ld bytes", offs);
|
|
txtries = 0;
|
|
txstate = HTX_DATA;
|
|
}
|
|
else
|
|
{
|
|
logerr("can't send file from requested offset %ld", offs);
|
|
p_tx_fclose(pi);
|
|
txstate = HTX_NextFile;
|
|
}
|
|
}
|
|
else if( offs == -1 )
|
|
{
|
|
pi->send->status = FSTAT_SKIPPED;
|
|
log("remote allready has file");
|
|
p_tx_fclose(pi);
|
|
txstate = HTX_NextFile;
|
|
}
|
|
else /* Send file later */
|
|
{
|
|
pi->send->status = FSTAT_REFUSED;
|
|
log("remote refused file");
|
|
p_tx_fclose(pi);
|
|
txstate = HTX_NextFile;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPKT_DATA:
|
|
if( rxstate == HRX_DATA || rxstate == HRX_RPosAck )
|
|
{
|
|
if( hi->isize < 4 )
|
|
{
|
|
log("Hydra: got invalid DATA packet (ignored)");
|
|
}
|
|
else if( hydra_getlong(hi->ibuf) != pi->recv->bytes_received )
|
|
{
|
|
long pos = hydra_getlong(hi->ibuf);
|
|
|
|
if( rxstate == HRX_RPosAck )
|
|
{
|
|
if( pos < last_datapos )
|
|
rxstate = HRX_RPOS;
|
|
}
|
|
else /* rxstate == HRX_DATA */
|
|
{
|
|
/* Increase RPOS packet ID number */
|
|
hi->recv_rpos_id++;
|
|
|
|
/* Decrease data block size */
|
|
hi->recv_block_size >>= 1;
|
|
hydra_align_block_size(&hi->recv_block_size);
|
|
|
|
rxtries = 0;
|
|
rxstate = HRX_RPOS;
|
|
}
|
|
last_datapos = pos;
|
|
}
|
|
else /* Write to the file */
|
|
{
|
|
hi->recv_block_size = hi->isize-4;
|
|
last_datapos = hydra_getlong(hi->ibuf);
|
|
|
|
timer_set(&deadtimer, hi->timeout_dead);
|
|
timer_set(&recvtimer, hi->timeout_recv);
|
|
|
|
if( rxstate == HRX_RPosAck )
|
|
rxstate = HRX_DATA;
|
|
|
|
switch(p_rx_writefile(hi->ibuf+4, hi->isize-4, pi)) {
|
|
case 0:
|
|
if( hi->rxwindow > 0 )
|
|
{
|
|
/*
|
|
* It is not the best idea to
|
|
* acknowledge every data packet,
|
|
* but is so easy to implement :)
|
|
*/
|
|
rxlastack = (long)pi->recv->bytes_received;
|
|
hydra_putlong(hi->obuf, (long)pi->recv->bytes_received);
|
|
hydra_queuepkt(hi, hi->obuf, 4, HPKT_DATAACK);
|
|
}
|
|
|
|
pi->recv->bytes_received += hi->isize-4;
|
|
break;
|
|
|
|
case -1: /* SKIP (non-destructive) */
|
|
pi->recv->status = FSTAT_REFUSED;
|
|
hi->recv_rpos_id++;
|
|
rxtries = 0;
|
|
rxstate = HRX_Skip;
|
|
break;
|
|
|
|
case -2: /* SKIP (destructive) */
|
|
pi->recv->status = FSTAT_SKIPPED;
|
|
hi->recv_rpos_id++;
|
|
rxtries = 0;
|
|
rxstate = HRX_Skip;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPKT_DATAACK:
|
|
if( txstate == HTX_DATA || txstate == HTX_DATAACK )
|
|
{
|
|
long ackpos = hydra_getlong(hi->ibuf);
|
|
|
|
if( ackpos >= pi->send->bytes_sent )
|
|
log("Hydra got DATAACK from feature");
|
|
else if( ackpos < txlastack )
|
|
log("Hydra got DATAACK from past");
|
|
else
|
|
{
|
|
txlastack = ackpos;
|
|
|
|
if( (txstate == HTX_DATAACK)
|
|
&& (pi->send->bytes_sent < txlastack + hi->txwindow) )
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_DATA;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPKT_RPOS:
|
|
if( txstate == HTX_DATA || txstate == HTX_DATAACK
|
|
|| txstate == HTX_XWAIT
|
|
|| txstate == HTX_EOF || txstate == HTX_EOFACK )
|
|
{
|
|
if( hi->isize < 10 )
|
|
{
|
|
log("Hydra: got invalid RPOS packet (ignored)");
|
|
}
|
|
else
|
|
{
|
|
long offset = hydra_getlong(hi->ibuf);
|
|
int blocksize = hydra_getword(hi->ibuf + 4);
|
|
long packetId = hydra_getlong(hi->ibuf + 6);
|
|
|
|
/* Check requested block size */
|
|
if( blocksize > 0 )
|
|
hydra_align_block_size(&blocksize);
|
|
|
|
if( packetId > 0 && packetId == hi->send_rpos_id )
|
|
{
|
|
log("Hydra Tx got duplicated RPOS packet (ignore)");
|
|
}
|
|
else if( offset >= 0 )
|
|
{
|
|
/* Reset EOF flag! */
|
|
pi->send->eofseen = FALSE;
|
|
|
|
/* Store ID of the processed RPOS */
|
|
hi->send_rpos_id = packetId;
|
|
|
|
if( fseek(pi->send->fp, offset, SEEK_SET) == 0
|
|
&& ftell(pi->send->fp) == offset )
|
|
{
|
|
txlastack = offset; /* Is it OK? */
|
|
|
|
log("Hydra Tx resend from %ld",
|
|
(long)offset);
|
|
|
|
pi->send->bytes_sent = offset;
|
|
|
|
if( blocksize && blocksize != hi->send_block_size )
|
|
{
|
|
log("Hydra Tx use %ld bytes blocks",
|
|
(long)blocksize);
|
|
hi->send_block_size = blocksize;
|
|
hi->send_good_blocks = 0;
|
|
}
|
|
|
|
if( txstate != HTX_XWAIT )
|
|
txstate = HTX_DATA;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra Tx can't resend from %ld", (long)offset);
|
|
|
|
pi->send->status = FSTAT_REFUSED;
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
}
|
|
else if( offset == -1 )
|
|
{
|
|
log("Hydra Tx skipping file");
|
|
|
|
/* Store ID of the processed RPOS */
|
|
hi->send_rpos_id = packetId;
|
|
|
|
pi->send->status = FSTAT_SKIPPED;
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
else
|
|
{
|
|
log("Hydra Tx refusing file");
|
|
|
|
/* Store ID of the processed RPOS */
|
|
hi->send_rpos_id = packetId;
|
|
|
|
pi->send->status = FSTAT_REFUSED;
|
|
txtries = 0;
|
|
txstate = HTX_EOF;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPKT_EOF:
|
|
if( rxstate == HRX_DATA )
|
|
{
|
|
if( hi->isize < 4 )
|
|
{
|
|
log("Hydra: got invalid EOF packet (ignored)");
|
|
}
|
|
else if( hydra_getlong(hi->ibuf) != pi->recv->bytes_received )
|
|
{
|
|
last_datapos = hydra_getlong(hi->ibuf);
|
|
|
|
/* Increase RPOS packet ID number */
|
|
hi->recv_rpos_id++;
|
|
|
|
/* Decrease data block size */
|
|
hi->recv_block_size >>= 1;
|
|
hydra_align_block_size(&hi->recv_block_size);
|
|
|
|
rxtries = 0;
|
|
rxstate = HRX_RPOS;
|
|
}
|
|
else
|
|
{
|
|
pi->recv->status = FSTAT_SUCCESS;
|
|
|
|
if( p_rx_fclose(pi) == 0 )
|
|
{
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_EOFACK);
|
|
rxtries = 0;
|
|
rxstate = HRX_FINFO;
|
|
}
|
|
else /* Error closing file */
|
|
{
|
|
pi->recv->status = FSTAT_REFUSED;
|
|
rxtries = 0;
|
|
rxstate = HRX_Skip;
|
|
}
|
|
}
|
|
}
|
|
else if( rxstate == HRX_Skip || rxstate == HRX_SkipAck )
|
|
{
|
|
if( hi->isize < 4 )
|
|
{
|
|
log("Hydra: got invalid EOF packet (ignored)");
|
|
}
|
|
else if( hydra_getlong(hi->ibuf) >= 0 )
|
|
{
|
|
/*
|
|
* Possible they didn't seen our
|
|
* RPOS(-2) packet. If we send EOFACK
|
|
* packet now, file will be lost! :(
|
|
* There is such "bug" in 'Argus'?
|
|
* So we will Skip it again, again..
|
|
*/
|
|
rxstate = HRX_Skip;
|
|
}
|
|
else
|
|
{
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_EOFACK);
|
|
(void)p_rx_fclose(pi);
|
|
recvtimer = 0;
|
|
rxtries = 0;
|
|
rxstate = HRX_FINFO;
|
|
}
|
|
}
|
|
else if( hi->isize == 4 && hydra_getlong(hi->ibuf) == -2 )
|
|
{
|
|
/*
|
|
* We will ack. all received EOF packets
|
|
* (even unexpected) with (-2) offset,
|
|
* it is not harmfull.
|
|
*/
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_EOFACK);
|
|
}
|
|
else
|
|
{
|
|
log("Hydra got unexpected EOF packet");
|
|
}
|
|
break;
|
|
|
|
case HPKT_EOFACK:
|
|
if( txstate == HTX_EOF || txstate == HTX_EOFACK )
|
|
{
|
|
if( pi->send )
|
|
{
|
|
if( pi->send->eofseen )
|
|
pi->send->status = FSTAT_SUCCESS;
|
|
p_tx_fclose(pi);
|
|
txstate = HTX_NextFile;
|
|
}
|
|
else
|
|
{
|
|
txtries = 0;
|
|
txstate = HTX_END;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPKT_END:
|
|
if( txstate == HTX_END || txstate == HTX_ENDACK )
|
|
{
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_END);
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_END);
|
|
hydra_queuepkt(hi, NULL, 0, HPKT_END);
|
|
txtries = 0;
|
|
txstate = HTX_DONE;
|
|
}
|
|
break;
|
|
|
|
case HPKT_IDLE:
|
|
timer_set(&recvtimer, hi->timeout_recv);
|
|
break;
|
|
|
|
case HPKT_DEVDATA:
|
|
log("Hydra: ignore DEVDATA packet");
|
|
break;
|
|
|
|
case HPKT_DEVDACK:
|
|
log("Hydra: ignore DEVDACK packet");
|
|
break;
|
|
|
|
default:
|
|
log("Hydra: unhandled packet type #%d", recv_rc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send queued packets and buffered data
|
|
*/
|
|
txtries = 0;
|
|
while( hi->oencpos > 0 || hi->n_pkts > 0 )
|
|
{
|
|
if( ++txtries > 20 )
|
|
gotoexit(PRC_ERROR);
|
|
|
|
if( tty_select(NULL, &send_ready, 30) < 0 )
|
|
gotoexit(PRC_ERROR);
|
|
|
|
if( !send_ready || hydra_send(hi) )
|
|
gotoexit(PRC_ERROR);
|
|
}
|
|
FLUSHOUT();
|
|
|
|
exit:
|
|
if( pi->send && pi->send->fp )
|
|
p_tx_fclose(pi);
|
|
if( pi->recv && pi->recv->fp )
|
|
p_rx_fclose(pi);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int hydra(s_protinfo *pi, bool files_after_freqs)
|
|
{
|
|
int rc;
|
|
s_hydrainfo hi;
|
|
|
|
log("start Hydra send+receive%s",
|
|
files_after_freqs ? " (RH1 mode)" : "");
|
|
|
|
hydra_init(&hi);
|
|
|
|
/*
|
|
* Send only file requests during first
|
|
* batch if EMSI 'RH1' flag specified
|
|
*/
|
|
if( files_after_freqs )
|
|
pi->reqs_only = TRUE;
|
|
|
|
rc = hydra_batch(&hi, pi);
|
|
|
|
if( rc == PRC_NOERROR )
|
|
{
|
|
if( files_after_freqs )
|
|
pi->reqs_only = FALSE;
|
|
|
|
rc = hydra_batch(&hi, pi);
|
|
}
|
|
|
|
hydra_deinit(&hi);
|
|
|
|
return rc;
|
|
}
|
|
|