1260 lines
45 KiB
C
1260 lines
45 KiB
C
/*
|
|
* binkleyforce -- unix FTN mailer project
|
|
*
|
|
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
|
|
* Copyright (c) 2012 Sergey Dorofeev, 2:5020/12000
|
|
*
|
|
* 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 "io.h"
|
|
#include "session.h"
|
|
#include "outbound.h"
|
|
#include "prot_common.h"
|
|
#include "prot_binkp.h"
|
|
|
|
#define BINKP_HEADER (2)
|
|
#define BINKP_MAXBLOCK (32768)
|
|
|
|
#define BINKP_BLK_CMD (1)
|
|
#define BINKP_BLK_DATA (2)
|
|
|
|
typedef enum {
|
|
frs_nothing,
|
|
frs_data,
|
|
frs_didget,
|
|
frs_skipping
|
|
} e_file_receive_status;
|
|
|
|
typedef struct {
|
|
e_binkp_mode mode;
|
|
int phase;
|
|
int subphase;
|
|
s_binkp_sysinfo *local_data;
|
|
s_binkp_sysinfo *remote_data;
|
|
s_protinfo *pi;
|
|
bool address_established; // indicates that remote address is verified and disallows further change
|
|
char extracmd[BINKP_MAXBLOCK+1];
|
|
bool extraislast;
|
|
bool password_received;
|
|
bool NR;
|
|
bool MB;
|
|
bool complete;
|
|
int batchsendcomplete;
|
|
int batchreceivecomplete;
|
|
bool waiting_got;
|
|
int batch_send_count;
|
|
int batch_recv_count;
|
|
e_file_receive_status frs;
|
|
int emptyloop;
|
|
bool continuesend;
|
|
int rc;
|
|
} s_binkp_state;
|
|
|
|
int binkp_getforsend(s_binkp_state *bstate, char *buf, int *block_type, unsigned short *block_length);
|
|
int binkp_doreceiveblock(s_binkp_state *bstate, char *buf, int block_type, unsigned short block_length);
|
|
void binkp_process_NUL(s_binkp_sysinfo *remote_data, char *buffer);
|
|
void binkp_process_ADR(char *buffer);
|
|
int binkp_auth_incoming(s_binkp_sysinfo *remote_data);
|
|
|
|
|
|
int binkp_loop(s_binkp_state *bstate) {
|
|
unsigned char readbuf[BINKP_HEADER+BINKP_MAXBLOCK+1];
|
|
unsigned char writebuf[BINKP_HEADER+BINKP_MAXBLOCK+1];
|
|
|
|
bstate->rc = PRC_NOERROR;
|
|
bstate->phase = 0;
|
|
bstate->subphase = 0;
|
|
bstate->extracmd[0] = -1;
|
|
bstate->extraislast = false;
|
|
bstate->password_received = false;
|
|
bstate->NR = false;
|
|
if(bstate->remote_data->options & BINKP_OPT_NR) {
|
|
bstate->NR = true;
|
|
}
|
|
bstate->MB = false;
|
|
if(bstate->remote_data->options & BINKP_OPT_MB) {
|
|
bstate->MB = true;
|
|
}
|
|
bstate->complete = false; // end in this mode (handshake or session)
|
|
bstate->batchsendcomplete = 0;
|
|
bstate->batchreceivecomplete = 0;
|
|
bstate->waiting_got = false;
|
|
bstate->batch_send_count = 0;
|
|
bstate->batch_recv_count = 0;
|
|
bstate->frs = frs_nothing;
|
|
bstate->emptyloop = 0;
|
|
bstate->continuesend = false;
|
|
|
|
uint16_t read_pos=0;
|
|
uint16_t read_blklen=0;
|
|
uint16_t want_read=BINKP_HEADER;
|
|
|
|
uint16_t write_pos=0;
|
|
uint16_t have_to_write=0;
|
|
|
|
int n, m;
|
|
bool no_more_to_send = false;
|
|
bool no_more_read = false;
|
|
bool canread, canwrite;
|
|
|
|
int timeout = conf_number(cf_binkp_timeout);
|
|
if( timeout==0 )
|
|
timeout = 60;
|
|
|
|
/* used for higher level calls */
|
|
int block_type;
|
|
unsigned short block_length;
|
|
|
|
// session criterium handshake criterium
|
|
while (!bstate->complete || bstate->waiting_got) {
|
|
DEB((D_24554, ">>> BinkP loop sendcpl: %d recvcpl: %d phase: %d subphase: %d", bstate->batchsendcomplete, bstate->batchreceivecomplete,
|
|
bstate->phase, bstate->subphase));
|
|
if (bstate->continuesend) {
|
|
no_more_to_send = false;
|
|
bstate->continuesend = false;
|
|
}
|
|
|
|
if (have_to_write==0 && (!no_more_to_send || bstate->extracmd[0]!=(char)-1)) {
|
|
m = binkp_getforsend(bstate, writebuf+BINKP_HEADER, &block_type, &block_length);
|
|
if(m==1 || m==3) {
|
|
//DEB((D_24554, "got block for sending %d %hu", block_type, block_length));
|
|
write_pos = 0;
|
|
have_to_write = block_length+BINKP_HEADER;
|
|
if( block_type == BINKP_BLK_CMD ) {
|
|
writebuf[0] = (block_length>>8)|0x80;
|
|
}
|
|
else if( block_type == BINKP_BLK_DATA ) {
|
|
writebuf[0] = (block_length>>8)&0x7f;
|
|
} else {
|
|
DEB((D_24554, "block for sending has invalid type, aborting"));
|
|
return PRC_ERROR;
|
|
}
|
|
writebuf[1] = block_length&0xff;
|
|
// TODO: FIX unprotected mode
|
|
if (bstate->mode==bmode_transfer && bstate->remote_data->options&BINKP_OPT_CRYPT)
|
|
encrypt_buf(writebuf, have_to_write, bstate->remote_data->keys_out);
|
|
}
|
|
if (m==2 || m==3) {
|
|
DEB((D_24554, "no more to send"));
|
|
no_more_to_send = true;
|
|
}
|
|
if (m==0) {
|
|
DEB((D_24554, "binkp: nothing to write"));
|
|
}
|
|
if (m<0 || m>3) {
|
|
DEB((D_24554, "getforsend error"));
|
|
return PRC_ERROR;
|
|
}
|
|
}
|
|
|
|
if (bstate->batchsendcomplete && bstate->batchreceivecomplete) {
|
|
DEB((D_24554, "batch is complete"));
|
|
if (bstate->MB && (bstate->batch_send_count || bstate->batch_recv_count)) {
|
|
DEB((D_24554, "starting one more batch"));
|
|
bstate->batchsendcomplete -= 1;
|
|
bstate->batchreceivecomplete -= 1;
|
|
//bstate->firstbatch = false;
|
|
bstate->batch_send_count = 0;
|
|
bstate->batch_recv_count = 0;
|
|
no_more_to_send = false;
|
|
bstate->phase = 0;
|
|
bstate->frs = frs_nothing;
|
|
want_read = BINKP_HEADER;
|
|
continue;
|
|
}
|
|
else {
|
|
if (bstate->waiting_got) {
|
|
DEB((D_24554, "waiting for all files have being confirmed"));
|
|
}
|
|
else {
|
|
DEB((D_24554, "finishing session"));
|
|
bstate->complete = true;
|
|
want_read = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEB((D_24554, "select read: %d write: %d", want_read, have_to_write));
|
|
if (want_read || have_to_write) {
|
|
n = tty_select(want_read?&canread:NULL, have_to_write?&canwrite:NULL, timeout);
|
|
if( n<0 ) {
|
|
log("binkp error on tty_select");
|
|
return PRC_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
DEB((D_24554, "empty loop %d", ++bstate->emptyloop));
|
|
if (bstate->emptyloop==10) {
|
|
log("eternal loop");
|
|
return PRC_ERROR;
|
|
}
|
|
}
|
|
|
|
if(want_read && canread) {
|
|
n = tty_read(readbuf+read_pos, want_read);
|
|
if( n<0 ) {
|
|
log("binkp: tty read error");
|
|
return PRC_ERROR;
|
|
}
|
|
else if (n==0) {
|
|
log("read: remote socket shutdown");
|
|
return PRC_REMOTEABORTED;
|
|
}
|
|
// TODO: FIX unprotected mode
|
|
if (bstate->mode==bmode_transfer && bstate->remote_data->options & BINKP_OPT_CRYPT) {
|
|
decrypt_buf(readbuf+read_pos, n, bstate->remote_data->keys_in);
|
|
} else {
|
|
DEB((D_24554,"binkp: not encripted transfer"));
|
|
};
|
|
DEB((D_24554, "got %d bytes", n));
|
|
want_read -= n;
|
|
read_pos += n;
|
|
if (read_pos == BINKP_HEADER) {
|
|
// have read header, want read body
|
|
DEB((D_24554, "it should be 0: %d", want_read));
|
|
want_read = ((uint16_t)(readbuf[0]&0x7F)<<8) | (uint16_t)readbuf[1];
|
|
DEB((D_24554, "got block header, block length %u", want_read));
|
|
} // no else here: if want_read may be zero here for zero length block
|
|
}
|
|
|
|
// no_more_read only signs that read thread do not keep connection anymore but messages should be processed
|
|
if (want_read==0 && read_pos) { // check every loop, not only just after read as accepting may be deferred
|
|
block_type = readbuf[0]&0x80? BINKP_BLK_CMD: BINKP_BLK_DATA;
|
|
block_length = read_pos - BINKP_HEADER;
|
|
DEB((D_24554, "binkp: complete block is received %d %hu", block_type, block_length));
|
|
m = binkp_doreceiveblock(bstate, readbuf+BINKP_HEADER, block_type, block_length);
|
|
DEB((D_24554, "binkp: dorecieveblock result is %d", m));
|
|
if(m==1) {
|
|
DEB((D_24554, "block is successfully accepted"));
|
|
read_pos = 0;
|
|
want_read = BINKP_HEADER;
|
|
} else if (m==2) {
|
|
DEB((D_24554, "block accepted and no more is needed in this mode"));
|
|
no_more_read = true;
|
|
read_pos = 0;
|
|
want_read = 0; //BINKP_HEADER;
|
|
}
|
|
else if (m==0) {
|
|
DEB((D_24554, "binkp: keeping buffer"));
|
|
}
|
|
else if (m==3) {
|
|
DEB((D_24554, "aborting session"));
|
|
bstate->complete = true;
|
|
//rc = HRC_OTHER_ERR;
|
|
}
|
|
else {
|
|
log("doreceiveblock error");
|
|
return PRC_ERROR;
|
|
}
|
|
}
|
|
|
|
if (have_to_write && canwrite) {
|
|
DEB((D_24554, "writing %d pos %d", have_to_write, write_pos));
|
|
n = tty_write(writebuf+write_pos, have_to_write);
|
|
if( n<0 ) {
|
|
log("binkp: tty write error");
|
|
return PRC_ERROR;
|
|
}
|
|
else if (n==0) {
|
|
log("write: remote socket shutdown");
|
|
return PRC_REMOTEABORTED;
|
|
}
|
|
DEB((D_24554, "%d bytes sent", n));
|
|
write_pos += n;
|
|
have_to_write -= n;
|
|
}
|
|
}
|
|
DEB((D_24554,"+++ BinkP Loop ended with rc=%d", bstate->rc));
|
|
return bstate->rc;
|
|
}
|
|
|
|
int binkp_outgoing(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
|
|
{
|
|
char *p;
|
|
init_keys(remote_data->keys_out, local_data->passwd ? local_data->passwd : "-");
|
|
init_keys(remote_data->keys_in, "-");
|
|
for (p=local_data->passwd ? local_data->passwd : "-"; *p; p++)
|
|
update_keys(remote_data->keys_in, (int)*p);
|
|
s_binkp_state s;
|
|
s.mode = bmode_outgoing_handshake;
|
|
s.local_data = local_data;
|
|
s.remote_data = remote_data;
|
|
s.pi = NULL;
|
|
s.address_established = false;
|
|
return binkp_loop(&s);
|
|
}
|
|
|
|
int binkp_incoming(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
|
|
{
|
|
s_binkp_state s;
|
|
s.mode = bmode_incoming_handshake;
|
|
s.local_data = local_data;
|
|
s.remote_data = remote_data;
|
|
s.pi = NULL;
|
|
s.address_established = false;
|
|
return binkp_loop(&s);
|
|
}
|
|
|
|
int binkp_transfer(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data, s_protinfo *pi)
|
|
{
|
|
DEB((D_24554, "start transfer"));
|
|
s_binkp_state s;
|
|
s.mode = bmode_transfer;
|
|
s.local_data = local_data;
|
|
s.remote_data = remote_data;
|
|
s.pi = pi;
|
|
return binkp_loop(&s);
|
|
}
|
|
|
|
|
|
// hanshake
|
|
// send
|
|
// all nuls
|
|
// address
|
|
// wait password
|
|
// send OK/ERR
|
|
// -- end handshake
|
|
// M_FILE
|
|
// (wait M_GET)
|
|
// data
|
|
// wait M_GOT
|
|
// next file
|
|
// send EOB
|
|
|
|
// recv
|
|
// accept NULs
|
|
// accept address
|
|
// accept password
|
|
// -- end handshake
|
|
// accept M_GET or
|
|
// accept M_FILE
|
|
// accept data
|
|
// accept next file or EOB
|
|
|
|
|
|
int binkp_getforsend(s_binkp_state *bstate, char *buf, int *block_type, unsigned short *block_length)
|
|
{
|
|
int my_sf, wr_pos;
|
|
int n; // read file
|
|
if (bstate->extracmd[0]!=(char)-1) {
|
|
DEB((D_24554, "Send cmd to remote %d %s", bstate->extracmd[0], bstate->extracmd+1));
|
|
buf[0] = bstate->extracmd[0];
|
|
strcpy(buf+1, bstate->extracmd+1);
|
|
*block_type = BINKP_BLK_CMD;
|
|
*block_length = strlen(buf+1)+1;
|
|
bstate->extracmd[0] = -1;
|
|
if (bstate->extraislast) {
|
|
bstate->phase = 100;
|
|
DEB((D_24554, "extracmd is last"));
|
|
bstate->complete = true;
|
|
}
|
|
return 1;
|
|
}
|
|
if (bstate->mode==bmode_incoming_handshake || bstate->mode==bmode_outgoing_handshake ) {
|
|
switch( bstate->phase ) {
|
|
case 0: // MD5 challenge
|
|
bstate->phase+=1;
|
|
bstate->subphase=0;
|
|
if( bstate->mode==bmode_incoming_handshake && bstate->local_data->challenge_length > 0 )
|
|
{
|
|
DEB((D_24554, "send challenge"));
|
|
char challenge[128];
|
|
string_bin_to_hex(challenge, bstate->local_data->challenge, bstate->local_data->challenge_length);
|
|
buf[0] = BPMSG_NUL;
|
|
sprintf(buf+1, "OPT CRAM-MD5-%s", challenge);
|
|
DEB((D_24554, "send M_NUL %s", buf+1));
|
|
*block_type = BINKP_BLK_CMD;
|
|
*block_length = strlen(buf+1)+1;
|
|
return 1;
|
|
}
|
|
|
|
case 1: // send sysinfo
|
|
DEB((D_24554, "send sysinfo"));
|
|
my_sf = bstate->subphase;
|
|
bstate->subphase+=1;
|
|
*block_type = BINKP_BLK_CMD;
|
|
switch( my_sf ) {
|
|
case 0:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "SYS %s", bstate->local_data->systname);
|
|
return 1;
|
|
case 1:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "ZYZ %s", bstate->local_data->sysop);
|
|
return 1;
|
|
case 2:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "LOC %s", bstate->local_data->location);
|
|
return 1;
|
|
case 3:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "PHN %s", bstate->local_data->phone);
|
|
return 1;
|
|
case 4:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "NDL %s", bstate->local_data->flags);
|
|
return 1;
|
|
case 5:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "TIME %s", bstate->local_data->timestr);
|
|
return 1;
|
|
case 6:
|
|
buf[0]=BPMSG_NUL;
|
|
*block_length = 1 + sprintf(buf+1, "VER %s %s/%d.%d",
|
|
bstate->local_data->progname, bstate->local_data->protname,
|
|
bstate->local_data->majorver, bstate->local_data->minorver);
|
|
DEB((D_24554, "send M_NUL %s", buf+1));
|
|
return 1;
|
|
case 7:
|
|
if (bstate->mode==bmode_outgoing_handshake) {
|
|
buf[0]=BPMSG_NUL;
|
|
strcpy(buf+1, "OPT MB CRYPT");
|
|
if (!nodelist_checkflag (state.node.flags, "NR"))
|
|
strcat(buf+1, " NR");
|
|
// ND is too complicated and have unclear gain
|
|
// seems not to remove files from inbound until successful session end is enough to eliminate dupes
|
|
// if (!nodelist_checkflag (state.node.flags, "ND"))
|
|
// strcat(buf+1, " ND");
|
|
// TODO: add GZ BZ2 and maybe EXTCMD
|
|
*block_length = 1 + strlen(buf+1);
|
|
DEB((D_24554, "send M_NUL %s", buf+1));
|
|
return 1;
|
|
}
|
|
// else skip subphase
|
|
my_sf += 1;
|
|
bstate->subphase += 1;
|
|
//case 8:
|
|
}
|
|
// here if subphase==8
|
|
bstate->phase += 1;
|
|
bstate->subphase = 0;
|
|
// p
|
|
|
|
case 2:
|
|
DEB((D_24554, "send address"));
|
|
bstate->phase += 1;
|
|
buf[0] = BPMSG_ADR;
|
|
wr_pos = 1;
|
|
int i;
|
|
for( i = 0; i < state.n_localaddr; i++ )
|
|
{
|
|
if (i) wr_pos += sprintf(buf+wr_pos, " ");
|
|
if (state.localaddrs[i].addr.point) {
|
|
wr_pos += sprintf(buf+wr_pos, "%d:%d/%d.%d@%s",
|
|
state.localaddrs[i].addr.zone, state.localaddrs[i].addr.net,
|
|
state.localaddrs[i].addr.node, state.localaddrs[i].addr.point,
|
|
state.localaddrs[i].addr.domain);
|
|
}
|
|
else {
|
|
wr_pos += sprintf(buf+wr_pos, "%d:%d/%d@%s",
|
|
state.localaddrs[i].addr.zone, state.localaddrs[i].addr.net,
|
|
state.localaddrs[i].addr.node,
|
|
state.localaddrs[i].addr.domain);
|
|
}
|
|
}
|
|
*block_type = BINKP_BLK_CMD;
|
|
*block_length = wr_pos;
|
|
DEB((D_24554, "send address: %s", buf+1));
|
|
return 1;
|
|
|
|
case 3: // send password on outgoing or pw confirmation on incoming
|
|
// special empty password is sent if there is no password for the remote addr
|
|
if (bstate->mode==bmode_incoming_handshake) {
|
|
if (bstate->password_received) {
|
|
DEB((D_24554, "send OK, password verified"));
|
|
buf[0] = BPMSG_OK;
|
|
*block_type = BINKP_BLK_CMD;
|
|
*block_length = 1;
|
|
bstate->phase += 1;
|
|
return 1;
|
|
}
|
|
DEB((D_24554, "waiting for password from remote"));
|
|
return 0; // nothing to send
|
|
}
|
|
else if (bstate->mode==bmode_outgoing_handshake) {
|
|
if (!bstate->address_established) {
|
|
DEB((D_24554, "address not received still"));
|
|
return 0;
|
|
}
|
|
DEB((D_24554, "send password"));
|
|
|
|
buf[0] = BPMSG_PWD;
|
|
*block_type = BINKP_BLK_CMD;
|
|
|
|
if( bstate->local_data->passwd[0] == '\0' ) {
|
|
*block_length = 1 + sprintf(buf+1, "-");
|
|
}
|
|
else if( bstate->remote_data->options & BINKP_OPT_MD5 ) {
|
|
char digest_bin[16];
|
|
char digest_hex[33];
|
|
|
|
if(bstate->remote_data->challenge_length==0) {
|
|
DEB((D_24554, "waiting for challenge"));
|
|
return 0;
|
|
}
|
|
md5_cram_get(bstate->local_data->passwd, bstate->remote_data->challenge,
|
|
bstate->remote_data->challenge_length, digest_bin);
|
|
|
|
/* Encode digest to the hex string */
|
|
string_bin_to_hex(digest_hex, digest_bin, 16);
|
|
|
|
*block_length = 1 + sprintf(buf+1, "CRAM-MD5-%s", digest_hex);
|
|
}
|
|
else {
|
|
*block_length = 1 + sprintf(buf+1, "%s", bstate->local_data->passwd);
|
|
}
|
|
bstate->phase += 1;
|
|
return 1;
|
|
}
|
|
else {
|
|
log("impossible mode");
|
|
return -1;
|
|
}
|
|
|
|
|
|
case 4:
|
|
;
|
|
char pbuf[32];
|
|
int pwset = 0;
|
|
|
|
if (bstate->mode==bmode_incoming_handshake) {
|
|
char * p;
|
|
DEB((D_24554, "incoming handshake is complete"));
|
|
bstate->complete = true;
|
|
|
|
for (i=0;i<state.n_remoteaddr;i++)
|
|
if( !session_get_password(state.remoteaddrs[i].addr, pbuf, sizeof(pbuf)) ){
|
|
pwset = 1;
|
|
init_keys(bstate->remote_data->keys_in, pbuf[0]?pbuf:"-");
|
|
init_keys(bstate->remote_data->keys_out, "-");
|
|
for (p=pbuf[0]?pbuf:"-"; *p; p++)
|
|
update_keys(bstate->remote_data->keys_out, (int)*p);
|
|
}
|
|
//TODO: FIX incoming options
|
|
}
|
|
else {
|
|
//TODO: Fix outgoing options
|
|
DEB((D_24554, "outgoing handshake: everything is sent"));
|
|
for (i=0;i<state.n_remoteaddr;i++)
|
|
if( !session_get_password(state.remoteaddrs[i].addr, pbuf, sizeof(pbuf)) ){
|
|
pwset = 1;
|
|
}
|
|
|
|
}
|
|
// We have password-protected link. Remote options are already set,
|
|
// Now we determine how to CRYPT or not to CRYPT our file transfer
|
|
// Our CRYPT flag is always sent, so if the other side have CRYPT
|
|
// and link is password is set - The CRYPT must be
|
|
// Literally: if we have no pass -> drop the CRYPT flag
|
|
if ( !pwset ) {
|
|
DEB((D_24554, "binkp handshake: password NOT set, dropping CRYPT"));
|
|
bstate->remote_data->options &= !BINKP_OPT_CRYPT;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
}
|
|
else if (bstate->mode == bmode_transfer) {
|
|
|
|
switch (bstate->phase) {
|
|
send_next_file:
|
|
case 0:
|
|
DEB((D_24554, "fetch file from queue"));
|
|
if (p_tx_fopen(bstate->pi, NULL)) {
|
|
DEB((D_24554, "queue empty"));
|
|
bstate->phase = 4;
|
|
goto send_EOB;
|
|
}
|
|
bstate->waiting_got = true;
|
|
bstate->batch_send_count += 1;
|
|
|
|
//send M_FILE -1
|
|
if (bstate->NR) {
|
|
DEB((D_24554, "send M_FILE with -1"));
|
|
buf[0] = BPMSG_FILE;
|
|
*block_length = 1+sprintf(buf+1, "%s %ld %ld -1", bstate->pi->send->net_name,
|
|
(long)bstate->pi->send->bytes_total, (long)bstate->pi->send->mod_time);
|
|
DEB((D_24554, "M_FILE: %s", buf+1));
|
|
*block_type = BINKP_BLK_CMD;
|
|
return 3; // no state change. phase would be changed to 1 by recv when M_GET is received
|
|
}
|
|
bstate->phase += 1;
|
|
|
|
case 1: //send M_FILE - M_GET forcibly sets this phase. M_GET must open needed file
|
|
DEB((D_24554, "send M_FILE"));
|
|
buf[0] = BPMSG_FILE;
|
|
*block_length = 1+sprintf(buf+1, "%s %zu %lu 0", bstate->pi->send->net_name,
|
|
bstate->pi->send->bytes_total, bstate->pi->send->mod_time);
|
|
DEB((D_24554, "M_FILE: %s", buf+1));
|
|
*block_type = BINKP_BLK_CMD;
|
|
bstate->phase += 1;
|
|
return 1;
|
|
|
|
case 2: //send file data
|
|
n = p_tx_readfile (buf, 4096, bstate->pi); // BINKP_MAXBLOCK
|
|
if (n>0) {
|
|
DEB((D_24554, "send data block len=%d", n));
|
|
*block_type = BINKP_BLK_DATA;
|
|
*block_length = n;
|
|
bstate->pi->send->bytes_sent += n;
|
|
return 1;
|
|
}
|
|
else if (n<0) {
|
|
log("p_tx_readfile error");
|
|
return -1;
|
|
}
|
|
DEB((D_24554, "file is sent"));
|
|
bstate->pi->send->status = FSTAT_WAITACK;
|
|
|
|
bstate->phase += 1;
|
|
|
|
case 3: //wait for acknowlede
|
|
|
|
if (bstate->pi->send->waitack) {
|
|
DEB((D_24554, "file must be acknowledged with M_GOT"));
|
|
int i;
|
|
bool ack = false;
|
|
for(i = 0; i < bstate->pi->n_sentfiles; i++ ) {
|
|
if (p_compfinfo(&bstate->pi->sentfiles[i], bstate->pi->send->net_name, bstate->pi->send->bytes_total, bstate->pi->send->mod_time) == 0) {
|
|
if (bstate->pi->sentfiles[i].status == FSTAT_SUCCESS) {
|
|
ack = true;
|
|
DEB((D_24554, "acknowledged"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!ack) {
|
|
DEB((D_24554, "wait for M_GOT"));
|
|
return 0;
|
|
}
|
|
DEB((D_24554, "M_GOT received, going to next file"));
|
|
} else {
|
|
DEB((D_24554, "do not wait M_GOT"));
|
|
}
|
|
bstate->phase = 0;
|
|
goto send_next_file;
|
|
|
|
send_EOB:
|
|
case 4:
|
|
DEB((D_24554, "send EOB n_sentfile=%d", bstate->pi->n_sentfiles));
|
|
buf[0] = BPMSG_EOB;
|
|
*block_type = BINKP_BLK_CMD;
|
|
*block_length = 1;
|
|
bstate->batchsendcomplete += 1;
|
|
bstate->phase += 1;
|
|
return 1;
|
|
|
|
case 5:
|
|
DEB((D_24554, "BinkP_GetForSend: nothing to send"));
|
|
return 2;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
log("invalid mode");
|
|
return -1;
|
|
}
|
|
log("unrecognized state, shutting down");
|
|
bstate->complete = true;
|
|
return 2;
|
|
|
|
}
|
|
|
|
#define PROTO_ERROR(msg) { log("error: %s", msg); bstate->rc = PRC_LOCALABORTED; \
|
|
bstate->extracmd[0] = BPMSG_ERR; strcpy(bstate->extracmd+1, msg); bstate->extraislast = true; return 1; }
|
|
|
|
|
|
int binkp_doreceiveblock(s_binkp_state *bstate, char *buf, int block_type, unsigned short block_length)
|
|
{
|
|
DEB((D_24554, "dorecieveblock_type: %d", block_type));
|
|
DEB((D_24554, "dorecieveblock_bytes: %x %x %x %x", buf[0], buf[1], buf[2], buf[3]));
|
|
switch (block_type) {
|
|
|
|
case BINKP_BLK_CMD:
|
|
if (block_length<1) {
|
|
PROTO_ERROR("Zero length command received")
|
|
}
|
|
buf[block_length] = 0; // fencing for easy processing
|
|
switch (buf[0]) {
|
|
case BPMSG_NUL: /* Site information, just logging */
|
|
DEB((D_24554, "received M_NUL len=%d", block_length));
|
|
DEB((D_24554, "M_NUL %s", buf+1));
|
|
binkp_process_NUL(bstate->remote_data, buf+1);
|
|
return 1;
|
|
case BPMSG_ADR: /* List of addresses */
|
|
DEB((D_24554, "received M_ADR len=%d", block_length));
|
|
if (bstate->address_established) {
|
|
PROTO_ERROR("remote tries to change address");
|
|
}
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0; // suspend !!!
|
|
binkp_process_ADR(buf+1);
|
|
|
|
if( !state.n_remoteaddr ) {
|
|
log("error: remote did not supplied any addresses");
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0; // suspend
|
|
bstate->extracmd[0] = BPMSG_BSY;
|
|
strcpy(bstate->extracmd+1, "No addresses was presented");
|
|
bstate->extraislast = true;
|
|
return 1;
|
|
}
|
|
|
|
if (bstate->mode == bmode_incoming_handshake) {
|
|
int i;
|
|
DEB((D_24554, "sending options"));
|
|
bstate->extracmd[0] = BPMSG_NUL;
|
|
bstate->extraislast = false;
|
|
sprintf(bstate->extracmd+1,"OPT MB CRYPT");
|
|
s_override ovr;
|
|
DEB((D_24554, "process adr overrides"));
|
|
for(i = 0; i < state.n_remoteaddr; i++) {
|
|
ovr.sFlags = "";
|
|
override_get (&ovr, state.remoteaddrs[i].addr, 0);
|
|
DEB((D_24554, "got adr overrides"));
|
|
if (nodelist_checkflag (ovr.sFlags, "NR")==0) {
|
|
strcat (bstate->extracmd+1, " NR");
|
|
break;
|
|
}
|
|
}
|
|
DEB((D_24554, "process adr overrides"));
|
|
}
|
|
// further use extracmd only for errors
|
|
|
|
if (bstate->mode == bmode_outgoing_handshake) {
|
|
// check that remote has the address we call
|
|
if( session_addrs_check_genuine(state.remoteaddrs, state.n_remoteaddr,
|
|
state.node.addr) ) {
|
|
log("error: remote does not have the called address");
|
|
bstate->extracmd[0] = BPMSG_ERR;
|
|
strcpy(bstate->extracmd+1, "Sorry, you are not who I need");
|
|
bstate->extraislast = true;
|
|
return 1;
|
|
}
|
|
// check that all addresses of remote has the same password
|
|
strncpy(bstate->remote_data->passwd, bstate->local_data->passwd, BINKP_MAXPASSWD);
|
|
bstate->remote_data->passwd[BINKP_MAXPASSWD] = '\0';
|
|
|
|
if( session_addrs_check(state.remoteaddrs, state.n_remoteaddr,
|
|
bstate->remote_data->passwd, NULL, 0) ) {
|
|
log("error: Security violation");
|
|
bstate->extracmd[0] = BPMSG_ERR;
|
|
strcpy(bstate->extracmd+1, "Security violation");
|
|
bstate->extraislast = true;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
bstate->address_established = true;
|
|
return 1;
|
|
case BPMSG_PWD: /* Session password */
|
|
DEB((D_24554, "received M_PWD len=%d", block_length));
|
|
if (bstate->mode != bmode_incoming_handshake) {
|
|
PROTO_ERROR("unexpected M_PWD");
|
|
}
|
|
if (!bstate->address_established) {
|
|
PROTO_ERROR("M_PWD before M_ADR");
|
|
}
|
|
|
|
strnxcpy(bstate->remote_data->passwd, buf+1, block_length);
|
|
memcpy(bstate->remote_data->challenge, bstate->local_data->challenge, BINKP_MAXCHALLENGE+1);
|
|
bstate->remote_data->challenge_length = bstate->local_data->challenge_length;
|
|
|
|
/* Do authorization */
|
|
if( binkp_auth_incoming(bstate->remote_data) ) {
|
|
log("error: invalid password");
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0; // suspend if extra is occupied
|
|
bstate->extracmd[0] = BPMSG_ERR;
|
|
strcpy(bstate->extracmd+1, "Security violation");
|
|
bstate->extraislast = true;
|
|
return 1;
|
|
}
|
|
// lock addresses
|
|
if( session_addrs_lock(state.remoteaddrs, state.n_remoteaddr) ) {
|
|
log("error locking addresses of the remote");
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0; // suspend if extra is occupied
|
|
bstate->extracmd[0] = BPMSG_BSY;
|
|
strcpy(bstate->extracmd+1, "All addresses are busy");
|
|
bstate->extraislast = true;
|
|
return 1;
|
|
}
|
|
else {
|
|
DEB((D_24554, "flag password received"));
|
|
bstate->password_received = true;
|
|
return 2;
|
|
}
|
|
break;
|
|
|
|
case BPMSG_FILE: /* File information */
|
|
DEB((D_24554, "received M_FILE len=%d", block_length));
|
|
if (bstate->mode != bmode_transfer) {
|
|
PROTO_ERROR("unexpected M_FILE");
|
|
}
|
|
s_bpfinfo recvfi;
|
|
if( binkp_parsfinfo(buf+1, &recvfi, true) ) {
|
|
log ("M_FILE parse error: %s", buf + 1);
|
|
DEB((D_24554, "M_FILE parse error: %s", buf + 1 ));
|
|
PROTO_ERROR("invalid M_FILE");
|
|
}
|
|
bstate->batch_recv_count += 1;
|
|
if (bstate->frs == frs_data) {
|
|
PROTO_ERROR("overlapping M_FILE received");
|
|
}
|
|
|
|
if (bstate->frs == frs_didget) {
|
|
DEB((D_24554, "is it what we want?"));
|
|
if( bstate->pi->recv && p_compfinfo(bstate->pi->recv, recvfi.fn, recvfi.sz, recvfi.tm) == 0
|
|
&& bstate->pi->recv->bytes_skipped == recvfi.offs && bstate->pi->recv->fp ) {
|
|
log("resuming %s from offset %d", recvfi.fn, recvfi.offs);
|
|
bstate->frs = frs_data;
|
|
return 1;
|
|
}
|
|
DEB((D_24554, "no, skipping; TODO: accept it"));
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0;
|
|
bstate->extracmd[0] = BPMSG_SKIP;
|
|
sprintf(bstate->extracmd+1, "%s %zu %lu %zu", recvfi.fn, recvfi.sz, recvfi.tm, recvfi.offs);
|
|
bstate->extraislast = false;
|
|
return 1;
|
|
}
|
|
|
|
if (bstate->frs!=frs_nothing && bstate->frs!=frs_skipping) {
|
|
DEB((D_24554, "strange receiving mode %d", bstate->frs));
|
|
PROTO_ERROR("invalid mode for M_FILE");
|
|
}
|
|
|
|
if( bstate->extracmd[0] != (char)-1 ) return 0;
|
|
switch(p_rx_fopen(bstate->pi, recvfi.fn, recvfi.sz, recvfi.tm, 0)) {
|
|
case 0:
|
|
if (bstate->pi->recv->bytes_skipped == recvfi.offs) {
|
|
log("accepting file %s from offset %d", recvfi.fn, recvfi.offs);
|
|
bstate->frs = frs_data;
|
|
return 1;
|
|
}
|
|
DEB((D_24554, "making M_GET to skip downloaded part, file: %s, size %ld, time %ld, mode 0",recvfi.fn,recvfi.sz, recvfi.tm ));
|
|
bstate->extracmd[0] = BPMSG_GET;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld %ld",
|
|
bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time,
|
|
(long)bstate->pi->recv->bytes_skipped);
|
|
bstate->extraislast = false;
|
|
bstate->frs = frs_didget;
|
|
return 1;
|
|
case 1:
|
|
DEB((D_24554, "SKIP, non-destructive"));
|
|
bstate->extracmd[0] = BPMSG_SKIP;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld", bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
bstate->frs = frs_skipping;
|
|
return 1;
|
|
case 2:
|
|
DEB((D_24554, "SKIP, destructive"));
|
|
bstate->extracmd[0] = BPMSG_GOT;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld",
|
|
bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
bstate->frs = frs_skipping;
|
|
return 1;
|
|
default:
|
|
PROTO_ERROR("p_rx_fopen_error");
|
|
}
|
|
PROTO_ERROR("never should get here");
|
|
|
|
case BPMSG_OK: /* Password was acknowleged (data ignored) */
|
|
DEB((D_24554, "received M_OK len=%d", block_length));
|
|
if (bstate->mode != bmode_outgoing_handshake) {
|
|
PROTO_ERROR("unexpected M_OK");
|
|
}
|
|
if (session_addrs_lock(state.remoteaddrs, state.n_remoteaddr)) {
|
|
log("error: unable to lock");
|
|
if (bstate->extracmd[0]!= (char)-1) return 0;
|
|
bstate->extracmd[0] = BPMSG_BSY;
|
|
strcpy(bstate->extracmd+1, "All addresses are busy");
|
|
bstate->extraislast = true;
|
|
return 2;
|
|
}
|
|
DEB((D_24554, "outoing handshake successfully complete"));
|
|
bstate->complete = true;
|
|
return 2;
|
|
|
|
case BPMSG_EOB: /* End Of Batch (data ignored) */
|
|
DEB((D_24554, "received M_EOB len=%d", block_length));
|
|
if (bstate->mode != bmode_transfer) {
|
|
PROTO_ERROR("unexpected M_EOB");
|
|
}
|
|
bstate->batchreceivecomplete += 1;
|
|
return 1; // continue receiving as M_GOT may and would arrive
|
|
|
|
case BPMSG_GOT: /* File received */
|
|
DEB((D_24554, "received M_GOT len=%d", block_length));
|
|
goto got_skip;
|
|
case BPMSG_SKIP:
|
|
DEB((D_24554, "received M_SKIP len=%d", block_length));
|
|
got_skip:
|
|
if (bstate->mode != bmode_transfer) {
|
|
PROTO_ERROR("unexpected M_GOT/M_SKIP");
|
|
}
|
|
s_bpfinfo fi;
|
|
int i;
|
|
if (binkp_parsfinfo (buf+1, &fi, false)) {
|
|
PROTO_ERROR("error parsing M_GOT/M_SKIP");
|
|
}
|
|
|
|
DEB((D_24554, "params: fn=%s sz=%d tm=%d", fi.fn, fi.sz, fi.tm));
|
|
|
|
if (strcmp (bstate->pi->send->net_name, fi.fn) == 0 && bstate->pi->send->status != FSTAT_WAITACK) {
|
|
DEB((D_24554, "aborting current file"));
|
|
if (bstate->pi->send->netspool) {
|
|
PROTO_ERROR("cannot SKIP or REFUSE netspool");
|
|
}
|
|
p_tx_fclose(bstate->pi);
|
|
bstate->phase = 0;
|
|
}
|
|
|
|
for(i = 0; i < bstate->pi->n_sentfiles; i++ ) {
|
|
if (p_compfinfo (&bstate->pi->sentfiles[i], fi.fn, fi.sz, fi.tm) == 0) {
|
|
s_finfo *tmp = bstate->pi->send;
|
|
bstate->pi->send = &bstate->pi->sentfiles[i];
|
|
if (buf[0] == BPMSG_SKIP) {
|
|
if (bstate->pi->send->netspool) {
|
|
PROTO_ERROR("cannot skip netspool");
|
|
}
|
|
log("skipped %s", fi.fn);
|
|
bstate->pi->send->status = FSTAT_REFUSED;
|
|
} else {
|
|
if (bstate->pi->send->status == FSTAT_WAITACK) {
|
|
DEB((D_24554, "confirmed %s", fi.fn));
|
|
bstate->pi->send->status = FSTAT_SUCCESS;
|
|
} else {
|
|
log("confirmed not sent file - skipped %s", fi.fn);
|
|
if (bstate->pi->send->netspool) {
|
|
PROTO_ERROR("cannot skip netspool");
|
|
}
|
|
bstate->pi->send->status = FSTAT_SKIPPED;
|
|
}
|
|
}
|
|
DEB((D_24554, "closing file"));
|
|
p_tx_fclose(bstate->pi);
|
|
bstate->pi->send = tmp;
|
|
goto check_that_all_files_are_confirmed;
|
|
}
|
|
}
|
|
PROTO_ERROR("unmatched file name in M_GOT/M_SKIP");
|
|
|
|
check_that_all_files_are_confirmed:
|
|
{
|
|
int j;
|
|
for (j = 0; j < bstate->pi->n_sentfiles; j++) {
|
|
if (bstate->pi->sentfiles[i].status == FSTAT_WAITACK) {
|
|
DEB((D_24554, "sent file %d waits for acknowlede", j));
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
DEB((D_24554, "all files are confirmed"));
|
|
bstate->waiting_got = false;
|
|
return 1;
|
|
|
|
|
|
case BPMSG_ERR: /* Misc errors */
|
|
log("remote error: %s", buf+1);
|
|
bstate->rc = PRC_REMOTEABORTED;
|
|
return 3;
|
|
case BPMSG_BSY: /* All AKAs are busy */
|
|
log("remote busy: %s", buf+1);
|
|
bstate->rc = PRC_REMOTEABORTED;
|
|
return 3;
|
|
|
|
case BPMSG_GET: /* Get a file from offset */
|
|
DEB((D_24554, "received M_GET len=%d", block_length));
|
|
if (bstate->mode != bmode_transfer) {
|
|
PROTO_ERROR("unexpected M_GET");
|
|
}
|
|
s_bpfinfo getfi;
|
|
if (binkp_parsfinfo(buf+1, &getfi, true) != 0) {
|
|
DEB((D_24554, "error parsing M_GET %s", buf+1));
|
|
PROTO_ERROR("invalid M_GET");
|
|
}
|
|
DEB((D_24554, "M_GET file %s size %d time %d offset %d", getfi.fn, getfi.sz, getfi.tm, getfi.offs));
|
|
|
|
if (bstate->extracmd[0] != (char)-1) return 0;
|
|
|
|
if (bstate->pi->send) if (p_compfinfo(bstate->pi->send, getfi.fn, getfi.sz, getfi.tm)==0) {
|
|
DEB((D_24554, "M_GET for currently transmitted file"));
|
|
if (getfi.offs==bstate->pi->send->bytes_sent) {
|
|
DEB((D_24554, "M_GET offset match current, seems NR mode"));
|
|
// go to sending M_FILE
|
|
bstate->phase = 2;
|
|
bstate->extracmd[0] = BPMSG_FILE;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld %ld",
|
|
bstate->pi->send->net_name, (long)bstate->pi->send->bytes_total,
|
|
(long)bstate->pi->send->mod_time, (long)bstate->pi->send->bytes_sent);
|
|
bstate->extraislast = false;
|
|
bstate->continuesend = true;
|
|
return 1;
|
|
|
|
}
|
|
}
|
|
|
|
if (bstate->pi->send) if (bstate->pi->send->netspool) {
|
|
DEB((D_24554, "ignore differing M_GET for netspool"));
|
|
bstate->continuesend = true;
|
|
return 1;
|
|
}
|
|
|
|
if (bstate->pi->send) if (p_compfinfo(bstate->pi->send, getfi.fn, getfi.sz, getfi.tm)==0) {
|
|
log("resending \"%s\" from %ld offset", bstate->pi->send->net_name, (long)getfi.offs);
|
|
if( p_tx_rewind(bstate->pi, getfi.offs) != 0 ) {
|
|
DEB((D_24554, "failed to rewind"));
|
|
p_tx_fclose(bstate->pi);
|
|
PROTO_ERROR("seek error")
|
|
}
|
|
bstate->pi->send->bytes_skipped = getfi.offs;
|
|
bstate->pi->send->bytes_sent = getfi.offs;
|
|
bstate->extracmd[0] = BPMSG_FILE;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld %ld", bstate->pi->send->net_name, (long)bstate->pi->send->bytes_total,
|
|
(long)bstate->pi->send->mod_time, (long)getfi.offs);
|
|
bstate->extraislast = false;
|
|
bstate->phase = 2;
|
|
bstate->continuesend = true;
|
|
return 1;
|
|
}
|
|
|
|
if( bstate->pi->send ) {
|
|
DEB((D_24554, "aborting current file"));
|
|
//p_tx_fclose(bstate->pi); may be requested again
|
|
}
|
|
|
|
s_filehint hint;
|
|
hint.fn = getfi.fn;
|
|
hint.sz = getfi.sz;
|
|
hint.tm = getfi.tm;
|
|
if( p_tx_fopen(bstate->pi, &hint) != 0 ) {
|
|
PROTO_ERROR("could not satisfy M_GET");
|
|
}
|
|
if( p_tx_rewind(bstate->pi, getfi.offs) != 0 ) {
|
|
DEB((D_24554, "failed to rewind"));
|
|
p_tx_fclose(bstate->pi);
|
|
PROTO_ERROR("seek error 2");
|
|
}
|
|
bstate->waiting_got = true;
|
|
log("sending \"%s\" from %ld offset", bstate->pi->send->net_name, (long)getfi.offs);
|
|
bstate->pi->send->bytes_skipped = getfi.offs;
|
|
bstate->pi->send->bytes_sent = getfi.offs;
|
|
bstate->extracmd[0] = BPMSG_FILE;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld %ld", bstate->pi->send->net_name, (long)bstate->pi->send->bytes_total,
|
|
(long)bstate->pi->send->mod_time, (long)getfi.offs);
|
|
bstate->extraislast = false;
|
|
bstate->phase = 2;
|
|
bstate->continuesend = true;
|
|
return 1;
|
|
}
|
|
DEB((D_24554, "unknown command %d received", buf[0]));
|
|
PROTO_ERROR("invalid command")
|
|
|
|
case BINKP_BLK_DATA:
|
|
//if there is file in progress
|
|
DEB((D_24554, "data block received length=%d", block_length));
|
|
if (block_length==0) {
|
|
log("warning: remote have sent zero length data block");
|
|
return 1;
|
|
}
|
|
if (bstate->frs == frs_nothing) {
|
|
PROTO_ERROR("unexpected data block");
|
|
}
|
|
if (bstate->frs == frs_didget || bstate->frs == frs_skipping) {
|
|
DEB((D_24554, "did M_GET or M_GOT or M_SKIP, skipping data"));
|
|
return 1;
|
|
}
|
|
|
|
if (bstate->extracmd[0] != (char)-1) return 0;
|
|
|
|
long int n;
|
|
n = p_rx_writefile(buf, block_length, bstate->pi);
|
|
|
|
if( n < 0 ) {
|
|
log("error writing local file");
|
|
if( n == (long int)-2 ) {
|
|
bstate->extracmd[0] = BPMSG_GOT;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld", bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
}
|
|
else {
|
|
bstate->extracmd[0] = BPMSG_SKIP;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld", bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
}
|
|
bstate->frs = frs_skipping;
|
|
p_rx_fclose(bstate->pi);
|
|
return 1;
|
|
}
|
|
else {
|
|
bstate->pi->recv->bytes_received += block_length;
|
|
|
|
/* Was it the last data block? */
|
|
if( bstate->pi->recv->bytes_received > bstate->pi->recv->bytes_total ) {
|
|
log("binkp got too many data (%ld, %ld expected)",
|
|
(long)bstate->pi->recv->bytes_received, (long)bstate->pi->recv->bytes_total);
|
|
|
|
bstate->frs = frs_skipping;
|
|
bstate->pi->recv->status = FSTAT_REFUSED;
|
|
p_rx_fclose(bstate->pi);
|
|
PROTO_ERROR("extra data for file")
|
|
}
|
|
else if( bstate->pi->recv->bytes_received == bstate->pi->recv->bytes_total )
|
|
{
|
|
DEB((D_24554, "receive completed"));
|
|
bstate->frs = frs_nothing;
|
|
bstate->pi->recv->status = FSTAT_SUCCESS;
|
|
if( !p_rx_fclose(bstate->pi) )
|
|
{
|
|
bstate->extracmd[0] = BPMSG_GOT;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld",
|
|
bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
DEB((D_24554, "some error committing file"));
|
|
bstate->extracmd[0] = BPMSG_SKIP;
|
|
sprintf(bstate->extracmd+1, "%s %ld %ld",
|
|
bstate->pi->recv->net_name, (long)bstate->pi->recv->bytes_total,
|
|
(long)bstate->pi->recv->mod_time);
|
|
bstate->extraislast = false;
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEB((D_24554, "data block accepted"));
|
|
return 1;
|
|
}
|
|
}
|
|
PROTO_ERROR("never should be here");
|
|
default:
|
|
PROTO_ERROR("impossible block_type");
|
|
}
|
|
}
|
|
|
|
|
|
// ---- inherent code ----
|
|
|
|
void binkp_process_NUL(s_binkp_sysinfo *remote_data, char *buffer)
|
|
{
|
|
|
|
|
|
if( strncmp(buffer, "SYS ", 4) == 0 )
|
|
strnxcpy(remote_data->systname, buffer+4, sizeof(remote_data->systname));
|
|
else if( strncmp(buffer, "ZYZ ", 4) == 0 )
|
|
strnxcpy(remote_data->sysop, buffer+4, sizeof(remote_data->sysop));
|
|
else if( strncmp(buffer, "LOC ", 4) == 0 )
|
|
strnxcpy(remote_data->location, buffer+4, sizeof(remote_data->location));
|
|
else if( strncmp(buffer, "PHN ", 4) == 0 )
|
|
strnxcpy(remote_data->phone, buffer+4, sizeof(remote_data->phone));
|
|
else if( strncmp(buffer, "NDL ", 4) == 0 )
|
|
strnxcpy(remote_data->flags, buffer+4, sizeof(remote_data->flags));
|
|
else if( strncmp(buffer, "TIME ", 5) == 0 )
|
|
strnxcpy(remote_data->timestr, buffer+5, sizeof(remote_data->timestr));
|
|
else if( strncmp(buffer, "OPT ", 4) == 0 )
|
|
{
|
|
if( *remote_data->opt )
|
|
{
|
|
strnxcat(remote_data->opt, " ", sizeof(remote_data->opt));
|
|
strnxcat(remote_data->opt, buffer+4, sizeof(remote_data->opt));
|
|
}
|
|
else
|
|
strnxcpy(remote_data->opt, buffer+4, sizeof(remote_data->opt));
|
|
DEB((D_24554,"Process OPT"));
|
|
binkp_parse_options(remote_data, buffer+4);
|
|
}
|
|
else if( strncmp(buffer, "VER ", 4) == 0 )
|
|
{
|
|
char *p;
|
|
/* <mailer> [<protocol>/<vermaj>.<vermin>] */
|
|
if( (p = strchr(buffer+4, ' ')) )
|
|
{
|
|
char *q;
|
|
strnxcpy(remote_data->progname, buffer+4,
|
|
MIN(sizeof(remote_data->progname), p - (buffer+4) + 1));
|
|
++p;
|
|
if( (q = strchr(p, '/')) )
|
|
{
|
|
strnxcpy(remote_data->protname, p,
|
|
MIN(sizeof(remote_data->protname), q - p + 1));
|
|
sscanf(q+1, "%d.%d",
|
|
&remote_data->majorver,
|
|
&remote_data->minorver);
|
|
}
|
|
if ((remote_data->majorver * 100 +
|
|
remote_data->minorver)> 100)
|
|
remote_data->options |= BINKP_OPT_MB;
|
|
}
|
|
else
|
|
strnxcpy(remote_data->progname, buffer+4, sizeof(remote_data->progname));
|
|
}
|
|
else if( strncmp(buffer, "TRF ", 4) == 0 ) {
|
|
// usually 24554 protocol mailers send only netmail size and arcmail+files size
|
|
DEB((D_24554, "process TRF"));
|
|
if( sscanf(buffer, "TRF %d %d", &remote_data->TRF_PKT, &remote_data->TRF_other)==2 ) {
|
|
remote_data->has_TRF = true;
|
|
}
|
|
}
|
|
else
|
|
log("BinkP NUL: \"%s\"", string_printable(buffer)); // NUL cannot be invalid as it is optional info
|
|
}
|
|
|
|
void binkp_process_ADR(char *buffer)
|
|
{
|
|
s_faddr addr;
|
|
char *p, *q;
|
|
DEB((D_24554, "process ADR: %s", buffer));
|
|
for( p = string_token(buffer, &q, NULL, 0); p; p = string_token(NULL, &q, NULL, 0) )
|
|
{
|
|
DEB((D_24554, "binkp_process_ADR got token \"%s\" ",p ));
|
|
if( ftn_addrparse(&addr, p, FALSE) )
|
|
log("BinkP got unparsable address \"%s\"", string_printable(p));
|
|
else {
|
|
session_addrs_add(&state.remoteaddrs, &state.n_remoteaddr, addr);
|
|
DEB((D_24554, "process ADR complete"));
|
|
}
|
|
}
|
|
}
|
|
|
|
int binkp_auth_incoming(s_binkp_sysinfo *remote_data)
|
|
{
|
|
if( remote_data->challenge_length > 0
|
|
&& strncmp(remote_data->passwd, "CRAM-MD5-", 9) == 0 )
|
|
{
|
|
//DEB((D_24554, "md5 auth addrs %s", remote_data->addrs));
|
|
//DEB((D_24554, "md5 auth anum %d", remote_data->anum));
|
|
//DEB((D_24554, "md5 auth passwd %s", remote_data->passwd + 9));
|
|
//DEB((D_24554, "md5 auth challenge %s", remote_data->challenge));
|
|
//DEB((D_24554, "md5 auth challenge len %d", remote_data->challenge_length));
|
|
return session_addrs_check(state.remoteaddrs,
|
|
state.n_remoteaddr,
|
|
remote_data->passwd + 9,
|
|
remote_data->challenge,
|
|
remote_data->challenge_length);
|
|
}
|
|
|
|
DEB((D_24554, "plain-text auth"));
|
|
return session_addrs_check(state.remoteaddrs, state.n_remoteaddr,
|
|
remote_data->passwd, NULL, 0);
|
|
}
|