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_binkp_misc.c

774 lines
20 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 "io.h"
#include "session.h"
#include "outbound.h"
#include "prot_common.h"
#include "prot_binkp.h"
/*****************************************************************************
* Initialise binkp state structure, allocate memory for buffers, etc.
*
* Arguments:
* bpi pointer to the binkp state structure (s_bpinfo)
*
* Return value:
* None
*/
void binkp_init_bpinfo(s_bpinfo *bpi)
{
memset(bpi, '\0', sizeof(s_bpinfo));
bpi->obuf = (char*)xmalloc(BINKP_MAX_BLKSIZE + BINKP_BLK_HDRSIZE + 1);
bpi->opos = 0;
bpi->optr = bpi->obuf;
bpi->inew = TRUE;
bpi->isize = -1;
bpi->ibuf = (char*)xmalloc(BINKP_MAX_BLKSIZE + BINKP_BLK_HDRSIZE + 1);
bpi->timeout = conf_number(cf_binkp_timeout);
if( !bpi->timeout )
bpi->timeout = 300;
}
/*****************************************************************************
* DeInitialise binkp state structure, release allocated memory, etc.
*
* Arguments:
* bpi pointer to the binkp state structure (s_bpinfo)
*
* Return value:
* None
*/
void binkp_deinit_bpinfo(s_bpinfo *bpi)
{
if( bpi->obuf )
free(bpi->obuf);
if( bpi->ibuf )
free(bpi->ibuf);
if( bpi->msgqueue )
free(bpi->msgqueue);
memset(bpi, '\0', sizeof(s_bpinfo));
}
/*****************************************************************************
* Fills s[0] and s[1] with binkp frame header using value of u
*
* Arguments:
* s pointer to the binkp's frame header (2 bytes)
* val value to put
*
* Return value:
* None
*/
void binkp_puthdr(char *s, unsigned int val)
{
s[0] = ( (unsigned long) val >> 8 ) & 0xff;
s[1] = ( (unsigned long) val ) & 0xff;
}
/*****************************************************************************
* Fills s[0] with binkp message type value
*
* Arguments:
* s pointer to the destination buffer
* msg message type
*
* Return value:
* None
*/
void binkp_putmsgtype(char *s, e_bpmsg msg)
{
s[0] = ( (unsigned int) msg ) & 0xff;
}
/*****************************************************************************
* Extract size from binkp frame header
*
* Arguments:
* s pointer to the source buffer
*
* Return value:
* Value, extracted from binkp header
*/
int binkp_gethdr(const char *s)
{
return ( (unsigned int) (((unsigned char) s[0]) & 0x7f) << 8 )
| ( (unsigned int) (((unsigned char) s[1]) & 0xff) );
}
/*****************************************************************************
* Extract message type from s[0]
*
* Arguments:
* s pointer to the source buffer (1 byte)
*
* Return value:
* Value, extracted from binkp header, or -1 if extracted message type is
* out of range
*/
int binkp_getmsgtype(char ch)
{
int val = ( ((unsigned char)ch) & 0x7f );
if( BPMSG_MIN > val || val > BPMSG_MAX )
return -1;
else
return val;
}
/*****************************************************************************
* Puts a message to the output messages queue. These msgs will be send
* right after the current data block
*
* Arguments:
* bpi binkp state structure
* msg message type
* s1 message text
* s2 message text (will be concatenated with s1)
*
* Return value:
* None
*/
void binkp_queuemsg(s_bpinfo *bpi, e_bpmsg msg, const char *s1, const char *s2)
{
bpi->msgqueue = xrealloc(bpi->msgqueue, sizeof(s_bpmsg)*(bpi->n_msgs + 1));
memset(&bpi->msgqueue[bpi->n_msgs], '\0', sizeof(s_bpmsg));
/* Set message type */
bpi->msgqueue[bpi->n_msgs].type = msg;
/* Set message data size (without frame header) */
bpi->msgqueue[bpi->n_msgs].size = 1;
if( s1 ) bpi->msgqueue[bpi->n_msgs].size += strlen(s1);
if( s2 ) bpi->msgqueue[bpi->n_msgs].size += strlen(s2);
/* Set message data */
bpi->msgqueue[bpi->n_msgs].data = xmalloc(bpi->msgqueue[bpi->n_msgs].size+3);
binkp_puthdr(bpi->msgqueue[bpi->n_msgs].data,
(unsigned)(bpi->msgqueue[bpi->n_msgs].size | 0x8000));
binkp_putmsgtype(bpi->msgqueue[bpi->n_msgs].data + 2, msg);
bpi->msgqueue[bpi->n_msgs].data[3] = '\0';
if( s1 ) strcat(bpi->msgqueue[bpi->n_msgs].data + 3, s1);
if( s2 ) strcat(bpi->msgqueue[bpi->n_msgs].data + 3, s2);
DEB((D_PROT, "binkp_queuemsg: queued message #%d, %ld bytes, \"%s\"",
(int)bpi->msgqueue[bpi->n_msgs].type,
(long)bpi->msgqueue[bpi->n_msgs].size,
(char*)bpi->msgqueue[bpi->n_msgs].data+3));
++bpi->n_msgs;
++bpi->msgs_in_batch;
}
/*****************************************************************************
* Sends a message using format string
*
* Arguments:
* bpi binkp state structure
* msg message type
* fmt pointer to the format string, (man printf)
*
* Return value:
* None
*/
void binkp_queuemsgf(s_bpinfo *bpi, e_bpmsg msg, const char *fmt, ...)
{
char msg_text[2048];
va_list ap;
va_start(ap, fmt);
#ifdef HAVE_SNPRINTF
vsnprintf(msg_text, sizeof(msg_text), fmt, ap);
#else
vsprintf(msg_text, fmt, ap);
#endif
va_end(ap);
binkp_queuemsg(bpi, msg, msg_text, NULL);
}
/*****************************************************************************
* Parse most common message arguments, send error message to remote
* if needed.
*
* Arguments:
* s pointer to the string to parse
* fn pointer to the pointer for extracted file name
* sz pointer to the extracted file size
* tm pointer to the extracted file modification time
* offs pointer to the extracted file offset
*
* Return value:
* non-zero value on error, zero on success
*/
int binkp_parsfinfo(char *str,char **fn,size_t *sz,time_t *tm,size_t *offs){
char *n;
char *p_fname = NULL;
char *p_size = NULL;
char *p_time = NULL;
char *p_offs = NULL;
static char *s = NULL;
/* Attention, offs may be NULL! */
ASSERT(str != NULL && fn != NULL && sz != NULL && tm != NULL);
DEB((D_PROT, "binkp_parsemsg: want parse \"%s\"", s));
if (s) free (s);
s = xstrcpy (str);
p_fname = string_token(s, &n, NULL, 0);
p_size = string_token(NULL, &n, NULL, 0);
p_time = string_token(NULL, &n, NULL, 0);
if( offs ) p_offs = string_token(NULL, &n, NULL, 0);
if( p_fname && p_size && p_time && (!offs || p_offs) )
{
if( ISDEC(p_size) && ISDEC(p_time) &&
(!offs || ISDEC(p_offs) || !strcmp (p_offs, "-1")) )
{
(*fn) = p_fname;
(*sz) = atol(p_size);
(*tm) = atol(p_time);
if( offs ) *offs = atol(p_offs);
DEB((D_PROT, "binkp_parsemsg: file = \"%s\", size = %d, time = %d",
*fn, *sz, *tm));
return 0;
}
}
return 1;
}
/*****************************************************************************
* Put message to a buffer
*
* Arguments:
* bpi binkp state structure
* msg message
*
* Return value:
* Zero on success, and non-zero value if no more free space left
* in the buffer
*/
int binkp_buffer_message(s_bpinfo *bpi, s_bpmsg *msg)
{
DEB((D_PROT, "binkp_buffer_message: buffer message #%d, %ld byte(s)",
(int)msg->type, (long)msg->size));
/* Check for possible internal error */
if( msg->size > BINKP_MAX_BLKSIZE )
{
log("size of msg we want to send is too big (%db)", msg->size);
return 1;
}
/* Is there space for the new msg? */
if( bpi->opos + msg->size > BINKP_MAX_BLKSIZE ) return 1;
if( msg->data )
{
memcpy(bpi->obuf + bpi->opos, msg->data, msg->size + BINKP_BLK_HDRSIZE);
bpi->opos += msg->size + BINKP_BLK_HDRSIZE;
free(msg->data);
msg->data = NULL;
}
return 0;
}
/*****************************************************************************
* Send as much buffered data as possible (non-blocking)
*
* Arguments:
* bpi binkp state structure
*
* Return value:
* Zero on success, and non-zero value on errors
*/
int binkp_send_buffer(s_bpinfo *bpi)
{
int n;
DEB((D_PROT, "binkp_send_buffer: sending %ld byte(s)", (long)bpi->opos));
n = tty_write(bpi->optr, bpi->opos);
if( n >= bpi->opos )
{ bpi->optr = bpi->obuf; bpi->opos = 0; }
else if( n > 0 )
{ bpi->optr += n; bpi->opos -= n; }
else
return -1;
return 0;
}
/*****************************************************************************
* Send as much buffered data or messages from the queue as possible
*
* Arguments:
* bpi binkp state structure
*
* Return value:
* Zero on success, and non-zero value on errors
*/
int binkp_send(s_bpinfo *bpi)
{
int i;
if( bpi->opos == 0 && bpi->msgqueue )
{
/*
* Buffer is empty and there are unsent msgs
*/
for( i = 0; i < bpi->n_msgs; i++ )
{
if( bpi->msgqueue[i].sent == FALSE )
{
if( binkp_buffer_message(bpi, &bpi->msgqueue[i]) )
break;
else
bpi->msgqueue[i].sent = TRUE;
}
}
/* If the message queue is empty, free it */
if( i >= bpi->n_msgs )
{
if( bpi->msgqueue )
{ free(bpi->msgqueue); bpi->msgqueue = NULL; }
bpi->n_msgs = 0;
}
}
return bpi->opos ? binkp_send_buffer(bpi) : 0;
}
/*****************************************************************************
* Flush all buffers and queues that can contain unsent data
*
* Arguments:
* bpi binkp state structure
* timeout abort flushing if this time will be exceeded
*
* Return value:
* Zero on success, and non-zero value on errors
*/
int binkp_flush_queue(s_bpinfo *bpi, int timeout)
{
bool send_ready = FALSE;
time_t flush_timer;
timer_set(&flush_timer, timeout);
while( bpi->opos > 0 || bpi->n_msgs > 0 )
{
if( timer_expired(flush_timer)
|| tty_select(NULL, &send_ready, 10) < 0 )
return -1;
if( send_ready && binkp_send(bpi) < 0 )
return -1;
}
return 0;
}
/*****************************************************************************
* Try to receive next message or data block in non-blocking mode
*
* Arguments:
* bpi binkp state structure
*
* Return value:
* One of the BPMSG_* values
*/
int binkp_recv(s_bpinfo *bpi)
{
int n = 0;
int size;
if( bpi->inew || bpi->isize == -1 )
{
bpi->inew = FALSE;
bpi->isize = -1;
size = BINKP_BLK_HDRSIZE;
}
else
size = bpi->isize;
if( size > 0 )
{
n = tty_read(bpi->ibuf + bpi->ipos, size - bpi->ipos);
if( n <= 0 ) return BPMSG_EXIT;
}
bpi->ipos += n;
if( bpi->ipos == size )
{
if( bpi->isize == -1 )
{
/* We got block header */
bpi->ipos = 0;
bpi->imsg = ((bpi->ibuf[0] >> 7) & 0377) ? TRUE : FALSE;
bpi->isize = binkp_gethdr(bpi->ibuf);
DEB((D_PROT, "binkp_recv: received header: %ld byte(s) (%s)",
(long)bpi->isize, bpi->imsg ? "msg" : "data"));
if( bpi->isize > BINKP_MAX_BLKSIZE )
{
log("internal error: got %ld bytes block size", bpi->isize);
return BPMSG_EXIT;
}
else if( bpi->isize == 0 )
{
bpi->ipos = 0;
bpi->inew = TRUE;
bpi->isize = -1;
if( bpi->imsg == TRUE )
log("zero length message from remote");
}
}
else /* We got whole data block/message */
{
if( bpi->imsg == TRUE ) /* Is it message? */
{
bpi->ipos = 0;
bpi->inew = TRUE;
bpi->ibuf[size] = '\0';
bpi->imsgtype = binkp_getmsgtype(bpi->ibuf[0]);
DEB((D_PROT, "binkp_recv: got message #%d, %ld byte(s), \"%s\"",
(int)bpi->imsgtype, bpi->isize, bpi->ibuf+1));
++bpi->msgs_in_batch;
if( bpi->imsgtype < 0 )
{
bpi->isize = -1;
log("got incorrect message type");
if( ++bpi->junkcount >= 5 )
{
log("junk count exceeded");
return BPMSG_EXIT;
}
return BPMSG_NONE;
}
return bpi->imsgtype;
}
else /* It is data */
{
bpi->ipos = 0;
bpi->inew = TRUE;
bpi->ibuf[size] = '\0';
DEB((D_PROT, "binkp_recv: got data block, %ld byte(s)",
(long)bpi->isize));
return BPMSG_DATA;
}
}
}
return BPMSG_NONE;
}
/*****************************************************************************
* Send (queue) system information to the remote
*
* Arguments:
* bpi binkp state structure
* binkp structure with the system information
*
* Return value:
* None
*/
void binkp_queue_sysinfo(s_bpinfo *bpi, s_binkp_sysinfo *binkp)
{
int i;
char *astr = NULL;
char abuf[BF_MAXADDRSTR+1];
if( !state.caller && binkp->challenge_length > 0 )
{
char challenge[128];
string_bin_to_hex(challenge, binkp->challenge, binkp->challenge_length);
binkp_queuemsgf(bpi, BPMSG_NUL, "OPT CRAM-MD5-%s", challenge);
}
binkp_queuemsg(bpi, BPMSG_NUL, "SYS ", binkp->systname);
binkp_queuemsg(bpi, BPMSG_NUL, "ZYZ ", binkp->sysop);
binkp_queuemsg(bpi, BPMSG_NUL, "LOC ", binkp->location);
binkp_queuemsg(bpi, BPMSG_NUL, "PHN ", binkp->phone);
binkp_queuemsg(bpi, BPMSG_NUL, "NDL ", binkp->flags);
binkp_queuemsg(bpi, BPMSG_NUL, "TIME ", binkp->timestr);
binkp_queuemsgf(bpi, BPMSG_NUL, "VER %s %s/%d.%d",
binkp->progname, binkp->protname,
binkp->majorver, binkp->minorver);
for( i = 0; i < binkp->anum; i++ )
{
if( i ) astr = xstrcat(astr, " ");
astr = xstrcat(astr, ftn_addrstr(abuf, binkp->addrs[i].addr));
}
if( astr )
{
binkp_queuemsg(bpi, BPMSG_ADR, NULL, astr);
free(astr);
}
if (state.caller)
{
char *szOpt = xstrcpy (" MB");
if (!nodelist_checkflag (state.node.flags, "NR"))
szOpt = xstrcat (szOpt, " NR");
if (!nodelist_checkflag (state.node.flags, "ND"))
szOpt = xstrcat (szOpt, " ND");
if (*szOpt)
binkp_queuemsg(bpi, BPMSG_NUL, "OPT", szOpt);
free (szOpt);
}
}
/*****************************************************************************
* Write options to the log
*
* Arguments:
* binkp structure with the system information
*
* Return value:
* None
*/
void binkp_log_options(s_binkp_sysinfo *remote)
{
if (remote->options & BINKP_OPT_MB) log ("We are in MB mode.");
if (remote->options & BINKP_OPT_NR) log ("We are in NR mode.");
}
/*****************************************************************************
* Write system information to the log
*
* Arguments:
* binkp structure with the system information
*
* Return value:
* None
*/
void binkp_log_sysinfo(s_binkp_sysinfo *binkp)
{
int i;
char abuf[BF_MAXADDRSTR+1];
if( binkp->anum )
for( i = 0; i < binkp->anum; i++ )
{
log(" Address : %s", ftn_addrstr(abuf, binkp->addrs[i].addr));
}
if( *binkp->systname && *binkp->phone )
log(" System : %s (%s)",
string_printable(binkp->systname),
string_printable(binkp->phone));
else if( *binkp->systname )
log(" System : %s",
string_printable(binkp->systname));
else if( *binkp->phone )
log(" Phone : %s",
string_printable(binkp->phone));
#ifdef BFORCE_LOG_PASSWD
if( *binkp->passwd )
log(" Password : %s", string_printable(binkp->passwd));
#endif
if( *binkp->opt )
log(" Options : %s", string_printable(binkp->opt));
if( *binkp->sysop && *binkp->location )
log(" SysOp : %s from %s",
string_printable(binkp->sysop),
string_printable(binkp->location));
else if( *binkp->sysop )
log(" SysOp : %s",
string_printable(binkp->sysop));
else if( *binkp->location )
log(" Location : %s",
string_printable(binkp->location));
if( *binkp->progname )
{
log(" Mailer : %s (%s/%d.%d)",
*binkp->progname ? string_printable(binkp->progname) : "?",
*binkp->protname ? string_printable(binkp->protname) : "?",
binkp->majorver, binkp->minorver);
}
if( *binkp->flags )
log(" Flags : %s", string_printable(binkp->flags));
if( *binkp->timestr )
log(" Time : %s", string_printable(binkp->timestr));
}
/*****************************************************************************
* Set our local system information
*
* Arguments:
* binkp structure where to store system information
* remote_addr remote main address (for setting best AKA)
* caller are we calling system?
*
* Return value:
* None
*/
void binkp_set_sysinfo(s_binkp_sysinfo *binkp, s_faddr *remote_addr, bool caller)
{
s_cval_entry *addr_ptr;
s_cval_entry *hide_ptr;
s_faddr *primary = NULL;
const char *p_systname = conf_string(cf_system_name);
const char *p_location = conf_string(cf_location);
const char *p_sysop = conf_string(cf_sysop_name);
const char *p_phone = conf_string(cf_phone);
const char *p_flags = conf_string(cf_flags);
/* free previously allocated memory */
if( binkp->addrs )
free(binkp->addrs);
memset(binkp, '\0', sizeof(s_binkp_sysinfo));
/* Set best primary address */
if( remote_addr )
{
primary = session_get_bestaka(*remote_addr);
/* Add primary address */
if( primary )
session_addrs_add(&binkp->addrs, &binkp->anum, *primary);
}
/* Add other AKAs */
for( addr_ptr = conf_first(cf_address); addr_ptr;
addr_ptr = conf_next(addr_ptr) )
{
for( hide_ptr = conf_first(cf_hide_our_aka); hide_ptr;
hide_ptr = conf_next(hide_ptr) )
{
if( !ftn_addrcomp(hide_ptr->d.falist.addr, addr_ptr->d.falist.addr) )
break;
}
if( !hide_ptr && primary != &addr_ptr->d.falist.addr )
session_addrs_add(&binkp->addrs, &binkp->anum,
addr_ptr->d.falist.addr);
}
if( binkp->anum == 0 )
log("warning: no addresses will be presented to remote");
/* session password */
if( caller )
session_get_password(state.node.addr, binkp->passwd, sizeof(binkp->passwd));
binkp->majorver = BINKP_MAJOR;
binkp->minorver = BINKP_MINOR;
strnxcpy(binkp->progname, BF_NAME"/"BF_VERSION"/"BF_OS, sizeof(binkp->progname));
strnxcpy(binkp->protname, BINKP_NAME, sizeof(binkp->protname));
strnxcpy(binkp->sysop, p_sysop ? p_sysop : "Unknown", sizeof(binkp->sysop));
strnxcpy(binkp->systname, p_systname ? p_systname : "Unknown", sizeof(binkp->systname));
strnxcpy(binkp->location, p_location ? p_location : "Unknown", sizeof(binkp->location));
strnxcpy(binkp->phone, p_phone ? p_phone : "Unknown", sizeof(binkp->phone));
strnxcpy(binkp->flags, p_flags ? p_flags : "BINKP", sizeof(binkp->flags));
time_string_format(binkp->timestr, sizeof(binkp->timestr), "%Y/%m/%d %H:%M:%S", 0);
/* Set session challenge string */
if( !caller )
{
long rnd = (long)random();
int pid = ((int)getpid()) ^ ((int)random());
long utm = (long)time(0);
binkp->options |= BINKP_OPT_MD5;
binkp->challenge[0] = (unsigned char)(rnd );
binkp->challenge[1] = (unsigned char)(rnd >> 8 );
binkp->challenge[2] = (unsigned char)(rnd >> 16);
binkp->challenge[3] = (unsigned char)(rnd >> 24);
binkp->challenge[4] = (unsigned char)(pid );
binkp->challenge[5] = (unsigned char)(pid >> 8 );
binkp->challenge[6] = (unsigned char)(utm );
binkp->challenge[7] = (unsigned char)(utm >> 8 );
binkp->challenge[8] = (unsigned char)(utm >> 16);
binkp->challenge[9] = (unsigned char)(utm >> 24);
binkp->challenge_length = 10;
}
}
void binkp_parse_options(s_binkp_sysinfo *binkp, char *options)
{
char *p, *n;
for( p = string_token(options, &n, NULL, 0); p;
p = string_token(NULL, &n, NULL, 0) )
{
if( !strcmp(p, "NR") )
binkp->options |= BINKP_OPT_NR;
else if( !strcmp(p, "MB") )
binkp->options |= BINKP_OPT_MB;
else if( !strcmp(p, "MPWD") )
binkp->options |= BINKP_OPT_MPWD;
else if( !strncmp(p, "CRAM-", 5) )
{
char *hash_types = p + 5;
char *challenge = strchr(hash_types, '-');
if( challenge )
{
char *pp, *nn;
*challenge++ = '\0';
for( pp = string_token(hash_types, &nn, "/", 0); pp;
pp = string_token(NULL, &nn, "/", 0) )
{
if( !strcmp(pp, "SHA1") )
binkp->options |= BINKP_OPT_SHA1;
else if( !strcmp(pp, "MD5") )
binkp->options |= BINKP_OPT_MD5;
else if( !strcmp(pp, "DES") )
binkp->options |= BINKP_OPT_DES;
}
if( strlen(challenge) > (2*sizeof(binkp->challenge)) )
log("binkp got too long challenge string");
else
binkp->challenge_length = string_hex_to_bin(
binkp->challenge, challenge);
}
else
log("binkp got invalid option: \"%s\"", string_printable(p));
}
}
}