This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/mbcico/yoohoo.c
2009-05-15 22:01:56 +02:00

660 lines
18 KiB
C

/*****************************************************************************
*
* $Id: yoohoo.c,v 1.25 2007/08/25 18:32:08 mbse Exp $
* Purpose ...............: Fidonet mailer
*
*****************************************************************************
* Copyright (C) 1997-2007
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* MBSE BBS is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MBSE BBS; see the file COPYING. If not, write to the Free
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*****************************************************************************/
/* Added modifications made on 9 Jun 1996 by P.Saratxaga
* - added "Hello" structure (from FTS-0006) so we can more easily
* parse it.
* - added domain support in hello packet (in Hello.name, reading after
* first \0 there is the domain)
* - added support for 16 bit product code
*/
#include "../config.h"
#include "../lib/mbselib.h"
#include "../lib/nodelist.h"
#include "../lib/users.h"
#include "../lib/mbsedb.h"
#include "statetbl.h"
#include "ttyio.h"
#include "session.h"
#include "config.h"
#include "emsi.h"
#include "hydra.h"
#include "rdoptions.h"
#include "wazoo.h"
#include "dietifna.h"
#include "yoohoo.h"
#include "inbound.h"
#include "callstat.h"
/*------------------------------------------------------------------------*/
/* YOOHOO<tm> CAPABILITY VALUES */
/*------------------------------------------------------------------------*/
#define Y_DIETIFNA 0x0001 /* Can do fast "FTS-0001" 0000 0000 0000 0001 */
#define FTB_USER 0x0002 /* Full-Tilt Boogie 0000 0000 0000 0010 */
#define ZED_ZIPPER 0x0004 /* Does ZModem, 1K blocks 0000 0000 0000 0100 */
#define ZED_ZAPPER 0x0008 /* Can do ZModem variant 0000 0000 0000 1000 */
#define DOES_IANUS 0x0010 /* Can do Janus 0000 0000 0001 0000 */
#define DOES_HYDRA 0x0020 /* Can do Hydra 0000 0000 0010 0000 */
#define Bit_6 0x0040 /* reserved by FTSC 0000 0000 0100 0000 */
#define Bit_7 0x0080 /* reserved by FTSC 0000 0000 1000 0000 */
#define Bit_8 0x0100 /* reserved by FTSC 0000 0001 0000 0000 */
#define Bit_9 0x0200 /* reserved by FTSC 0000 0010 0000 0000 */
#define Bit_a 0x0400 /* reserved by FTSC 0000 0100 0000 0000 */
#define Bit_b 0x0800 /* reserved by FTSC 0000 1000 0000 0000 */
#define Bit_c 0x1000 /* reserved by FTSC 0001 0000 0000 0000 */
#define Bit_d 0x2000 /* reserved by FTSC 0010 0000 0000 0000 */
#define DO_DOMAIN 0x4000 /* Packet contains domain 0100 0000 0000 0000 */
#define WZ_FREQ 0x8000 /* WZ file req. ok 1000 0000 0000 0000 */
#define LOCALCAPS (Y_DIETIFNA|ZED_ZIPPER|ZED_ZAPPER|DOES_HYDRA|DO_DOMAIN)
static int rxyoohoo(void);
static int txyoohoo(void);
static void fillhello(unsigned short,char*);
static int checkhello(void);
static int y_akas;
static int iscaller;
static struct _hello {
unsigned char data[128];
unsigned char crc[2];
} hello;
typedef struct _Hello {
unsigned short signal; /* always 'o' (0x6f) */
unsigned short hello_version; /* currently 1 (0x01) */
unsigned short product; /* product code */
unsigned short product_maj; /* major revision of the product */
unsigned short product_min; /* minor revision of the product */
unsigned char my_name[60]; /* Other end's name, will include domain */
/* if DO_DOMAIN is set in capabilities */
unsigned char sysop[20]; /* sysop's name */
unsigned short my_zone; /* 0== not supported */
unsigned short my_net; /* out primary net number */
unsigned short my_node; /* our primary node number */
unsigned short my_point; /* 0 == not supported */
unsigned char my_password[8]; /* This isn't necessarily null-terminated */
unsigned char reserved2[8]; /* reserved by Opus */
unsigned short capabilities; /* see below */
unsigned char reserved3[12]; /* for non-Opus systems with "approval" */
/* total size 128 bytes */
} Hello;
extern int Loaded;
extern pid_t mypid;
extern int laststat;
extern int session_state;
Hello hello2;
Hello gethello2(unsigned char[]);
int rx_yoohoo(void)
{
int rc, protect = FALSE;
unsigned short capabilities,localcaps;
char *pwd = NULL;
callstat *cst;
pwd = NULL;
y_akas = 0;
localcaps = LOCALCAPS;
if (localoptions & NOZMODEM) localcaps &= ~(ZED_ZAPPER|ZED_ZIPPER);
if (localoptions & NOZEDZAP) localcaps &= ~ZED_ZAPPER;
if (localoptions & NOHYDRA) localcaps &= ~DOES_HYDRA;
emsi_local_opts = 0;
emsi_remote_opts = 0;
iscaller = 0;
if ((rc = rxyoohoo()) == 0) {
Loaded = checkhello();
if (y_akas == 0) {
Syslog('+', "All akas busy, abort");
return MBERR_SESSION_ERROR;
}
capabilities = hello2.capabilities;
if (capabilities & WZ_FREQ)
session_flags |= SESSION_WAZOO;
else
session_flags &= ~SESSION_WAZOO;
localcaps &= capabilities;
if (localcaps & DOES_HYDRA)
localcaps &= DOES_HYDRA;
else if (localcaps & ZED_ZAPPER)
localcaps &= ZED_ZAPPER;
else if (localcaps & ZED_ZIPPER)
localcaps &= ZED_ZIPPER;
else if (localcaps & FTB_USER)
localcaps &= FTB_USER;
else if (localcaps & Y_DIETIFNA)
localcaps &= Y_DIETIFNA;
if ((localoptions & NOFREQS) == 0)
localcaps |= WZ_FREQ;
else
emsi_local_opts |= OPT_NRQ;
if (((nlent=getnlent(remote->addr))) && (nlent->pflag != NL_DUMMY)) {
Syslog('+', "Remote is a listed system");
strncpy(history.location, nlent->location, 35);
UserCity(mypid, nlent->sysop, nlent->location);
}
cst = getstatus(remote->addr);
if (cst->trystat)
laststat = cst->trystat;
Syslog('s', "Last session status %d", laststat);
if (nlent)
rdoptions(Loaded);
if (strlen(nodes.Spasswd)) {
if ((strncasecmp((char*)hello2.my_password, nodes.Spasswd, strlen(nodes.Spasswd)) == 0) &&
(strlen((char*)hello2.my_password) == strlen(nodes.Spasswd))) {
Syslog('+', "Password correct, protected mail session");
protect = TRUE;
pwd = xstrcpy(nodes.Spasswd);
} else {
if (pwd)
free(pwd);
pwd = xstrcpy((char *)"BAD_PASS");
Syslog('?', "Remote password \"%s\", expected \"%s\"", (char*)hello2.my_password, nodes.Spasswd);
localcaps = 0;
}
} else
Syslog('s', "No YooHoo password check");
inbound_open(remote->addr, protect, FALSE);
fillhello(localcaps,pwd);
rc = txyoohoo();
if (pwd)
free(pwd);
}
if ((rc == 0) && ((localcaps & LOCALCAPS) == 0)) {
Syslog('+', "No common protocols or bad password");
return 0;
}
if (rc)
return MBERR_YOOHOO;
IsDoing("Inbound %s", ascfnode(remote->addr, 0x0f));
if (protect)
session_state = STATE_SECURE;
else
session_state = STATE_UNSECURE;
session_flags |= SESSION_WAZOO;
if (localcaps & DOES_HYDRA)
return hydra(0);
else if ((localcaps & ZED_ZAPPER) || (localcaps & ZED_ZIPPER)) {
if (localcaps & ZED_ZAPPER)
emsi_local_protos = PROT_ZAP;
else
emsi_local_protos = PROT_ZMO;
return rxwazoo();
} else if (localcaps & Y_DIETIFNA)
return rxdietifna();
WriteError("YooHoo internal error - no proto for 0x%04xh",localcaps);
return MBERR_YOOHOO;
}
int tx_yoohoo(void)
{
int rc;
unsigned short capabilities;
char *pwd;
y_akas = 0;
if (strlen(nodes.Spasswd))
pwd = xstrcpy(nodes.Spasswd);
else
pwd = NULL;
capabilities = LOCALCAPS;
if (localoptions & NOZMODEM)
capabilities &= ~(ZED_ZAPPER|ZED_ZIPPER);
if (localoptions & NOZEDZAP)
capabilities &= ~ZED_ZAPPER;
if (localoptions & NOHYDRA)
capabilities &= ~DOES_HYDRA;
if ((localoptions & NOFREQS) == 0)
capabilities |= WZ_FREQ;
else
emsi_local_opts |= OPT_NRQ;
fillhello(capabilities,pwd);
iscaller=1;
if ((rc = txyoohoo()) == 0) {
rc = rxyoohoo();
checkhello();
capabilities = hello2.capabilities;
if (capabilities & WZ_FREQ)
session_flags |= SESSION_WAZOO;
else
session_flags &= ~SESSION_WAZOO;
}
if (y_akas == 0) {
Syslog('+', "All akas busy, abort");
return MBERR_SESSION_ERROR;
}
if ((rc == 0) && ((capabilities & LOCALCAPS) == 0)) {
Syslog('+', "No common protocols");
return MBERR_SESSION_ERROR;
}
if (rc)
return MBERR_YOOHOO;
IsDoing("Outbound %s", ascfnode(remote->addr, 0x0f));
session_state = STATE_SECURE;
session_flags |= SESSION_WAZOO;
if (capabilities & DOES_HYDRA)
return hydra(1);
else if ((capabilities & ZED_ZAPPER) || (capabilities & ZED_ZIPPER)) {
if (capabilities & ZED_ZAPPER)
emsi_local_protos = PROT_ZAP;
else
emsi_local_protos = PROT_ZMO;
return txwazoo();
} else if (capabilities & Y_DIETIFNA)
return txdietifna();
WriteError("YooHoo internal error - no proto for 0x%04xh",capabilities);
return MBERR_YOOHOO;
}
SM_DECL(rxyoohoo,(char *)"rxyoohoo")
SM_STATES
sendenq,
waitchar,
getpacket,
sendnak,
sendack
SM_NAMES
(char *)"sendenq",
(char *)"waitchar",
(char *)"getpacket",
(char *)"sendnak",
(char *)"sendack"
SM_EDECL
int c, count=0;
unsigned short lcrc, rcrc;
SM_START(sendenq)
SM_STATE(sendenq)
if (count++ > 12) {
Syslog('+', "Too many tries to get YooHoo hello packet");
SM_ERROR;
}
PUTCHAR(ENQ);
SM_PROCEED(waitchar)
SM_STATE(waitchar)
c=GETCHAR(10);
if (c == TIMEOUT) {
SM_PROCEED(sendenq);
} else if (c < 0) {
SM_ERROR;
} else switch (c) {
case 0x1f: SM_PROCEED(getpacket);
break;
case YOOHOO: SM_PROCEED(sendenq);
break;
default: SM_PROCEED(waitchar);
break;
}
SM_STATE(getpacket)
GET((char*)&hello, sizeof(hello), 30);
if (STATUS) {
SM_ERROR;
}
lcrc = crc16xmodem((char*)hello.data, sizeof(hello.data));
rcrc = (hello.crc[0] << 8) + hello.crc[1];
if (lcrc != rcrc) {
Syslog('+',"crc does not match in YooHoo hello packet: %04xh/%04xh", rcrc,lcrc);
SM_PROCEED(sendnak);
} else {
SM_PROCEED(sendack);
}
SM_STATE(sendnak)
if (count++ > 9) {
Syslog('+', "Too many tries to get YooHoo hello packet");
SM_ERROR;
}
PUTCHAR('?');
SM_PROCEED(waitchar);
SM_STATE(sendack)
PUTCHAR(ACK);
SM_SUCCESS;
SM_END
SM_RETURN
SM_DECL(txyoohoo,(char *)"txyoohoo")
SM_STATES
sendyoohoo,
waitenq,
sendpkt,
waitchar
SM_NAMES
(char *)"sendyoohoo",
(char *)"waitenq",
(char *)"sendpkt",
(char *)"waitchar"
SM_EDECL
int c, count = 0, startstate;
unsigned short lcrc;
if (iscaller)
startstate = sendpkt;
else
startstate = sendyoohoo;
lcrc = crc16xmodem((char*)hello.data, sizeof(hello.data));
hello.crc[0] = lcrc >> 8;
hello.crc[1] = lcrc & 0xff;
SM_START(startstate)
SM_STATE(sendyoohoo)
PUTCHAR(YOOHOO);
SM_PROCEED(waitenq);
SM_STATE(waitenq)
c=GETCHAR(10);
if (c == TIMEOUT) {
if (count++ > 9) {
Syslog('+', "Timeout waiting ENQ");
SM_ERROR;
} else {
SM_PROCEED(sendyoohoo);
}
} else if (c < 0) {
SM_ERROR;
} else switch (c) {
case ENQ: SM_PROCEED(sendpkt);
case YOOHOO: SM_PROCEED(waitenq);
default: Syslog('+',"Got '%s' waiting hello ACK", printablec(c));
SM_PROCEED(waitchar);
break;
}
SM_STATE(sendpkt)
if (count++ > 9) {
Syslog('+', "Too many tries to send YooHoo hello packet");
SM_ERROR;
}
PUTCHAR(0x1f);
PUT((char*)&hello, sizeof(hello));
if (STATUS) {
SM_ERROR;
}
SM_PROCEED(waitchar);
SM_STATE(waitchar)
c=GETCHAR(30);
if (c == TIMEOUT) {
Syslog('+', "Timeout waiting hello ACK");
SM_ERROR;
} else if (c < 0) {
SM_ERROR;
} else switch (c) {
case ACK: SM_SUCCESS;
break;
case '?': SM_PROCEED(sendpkt);
break;
case ENQ: SM_PROCEED(sendpkt);
break;
default: SM_PROCEED(waitchar);
break;
}
SM_END
SM_RETURN
void fillhello(unsigned short capabilities, char *password)
{
faddr *best;
unsigned short majver, minver;
best = bestaka_s(remote->addr);
sscanf(VERSION,"%hd.%hd", &majver, &minver);
memset(&hello, 0, sizeof(hello));
hello.data[0] = 'o'; /* signal */
hello.data[2] = 1; /* hello-version */
hello.data[4] = (PRODCODE & 0x00ff); /* product code */
hello.data[5] = (PRODCODE & 0xff00) >> 8; /* product code */
hello.data[6] = (VERSION_MAJOR & 0x00ff); /* prod-ver-major */
hello.data[7] = (VERSION_MAJOR & 0xff00) >> 8; /* prod-ver-major */
hello.data[8] = (VERSION_MINOR & 0x00ff); /* prod-ver-minor */
hello.data[9] = (VERSION_MINOR & 0xff00) >> 8; /* prod-ver-minor */
strncpy((char*)hello.data+10, name?name:"Unavailable",59); /* name */
if (name) {
hello.data[10+strlen(name)] = '\0';
strncpy((char*)hello.data+11 + strlen(name),
best->domain, 58-strlen(name)); /* domain */
} else
strncpy((char*)hello.data + 22, best->domain, 47); /* domain */
strncpy((char*)hello.data+70, CFG.sysop_name,19); /* sysop */
hello.data[90] = best->zone&0xff; /* zone */
hello.data[91] = best->zone>>8; /* zone */
hello.data[92] = best->net&0xff; /* net */
hello.data[93] = best->net>>8; /* net */
hello.data[94] = best->node&0xff; /* node */
hello.data[95] = best->node>>8; /* node */
hello.data[96] = best->point&0xff; /* point */
hello.data[97] = best->point>>8; /* point */
if (password)
strncpy((char*)hello.data + 98, password, 8);
hello.data[114] = capabilities & 0xff; /* capabilities */
hello.data[115] = capabilities >> 8; /* capabilities */
tidy_faddr(best);
}
int checkhello(void)
{
unsigned short i, majver = 0, minver = 0;
fa_list **tmpl, *tmpa;
faddr remaddr;
char *prodnm, *q;
int loaded = FALSE;
Syslog('s',"check hello \"%s\"",printable((char*)hello.data,128));
hello2 = gethello2(hello.data);
if ((hello2.signal != 0x6f) || (hello2.hello_version != 0x01)) {
Syslog('+', "Got \"%s\" instead of \"o\\000\\001\000\"", printable((char*)hello.data,3));
}
prodnm = xstrcpy((char *)"<unknown program>");
for (i = 0; ftscprod[i].name; i++) {
if (ftscprod[i].code == hello2.product) {
free(prodnm);
prodnm = xstrcpy(ftscprod[i].name);
majver = hello2.product_maj;
minver = hello2.product_min;
break;
}
}
remaddr.zone = hello2.my_zone;
remaddr.net = hello2.my_net;
remaddr.node = hello2.my_node;
remaddr.point = hello2.my_point;
remaddr.name = NULL;
remaddr.domain = NULL;
if (hello2.my_name[0])
remaddr.domain = (char *)hello2.my_name + (strlen((char *)hello2.my_name)) + 1;
if (remaddr.domain[0]) {
if ((q = strchr(remaddr.domain, '.')))
*q = '\0';
} else {
remaddr.domain = NULL;
}
if (remote)
Syslog('s',"Remote known address: %s",ascfnode(remote->addr,0x1f));
for (tmpl = &remote; *tmpl; tmpl = &((*tmpl)->next));
if ((remote == NULL) || (metric(remote->addr, &remaddr) != 0)) {
(*tmpl) = (fa_list*)malloc(sizeof(fa_list));
(*tmpl)->next = NULL;
(*tmpl)->addr = (faddr*)malloc(sizeof(faddr));
(*tmpl)->addr->zone = remaddr.zone;
(*tmpl)->addr->net = remaddr.net;
(*tmpl)->addr->node = remaddr.node;
(*tmpl)->addr->point = remaddr.point;
(*tmpl)->addr->domain = xstrcpy(remaddr.domain);
(*tmpl)->addr->name = NULL; /* Added 15-Dec-1998 */
} else {
tmpl=&remote;
Syslog('s',"Using single remote address");
}
tmpl = &remote;
while (*tmpl) {
if (nodelock((*tmpl)->addr, mypid)) {
Syslog('+', " address: %s is locked, removed from aka list",ascfnode((*tmpl)->addr,0x1f));
tmpa = *tmpl;
*tmpl = (*tmpl)->next;
tidy_faddr(tmpa->addr);
free(tmpa);
} else {
Syslog('+', " address: %s",ascfnode((*tmpl)->addr,0x1f));
y_akas++;
/*
* With the loaded flag we prevent removing the noderecord
* when the remote presents us an address we don't know about.
*/
if (!Loaded) {
if (noderecord((*tmpl)->addr))
Loaded = TRUE;
}
tmpl = &((*tmpl)->next);
}
}
if (y_akas) { /* Only set if we have aka's left */
history.aka.zone = remote->addr->zone;
history.aka.net = remote->addr->net;
history.aka.node = remote->addr->node;
history.aka.point = remote->addr->point;
}
if (hello2.product < 0x0100)
Syslog('+', " uses: %s [%02X] version %d.%d", prodnm, hello2.product, majver, minver);
else
Syslog('+', " uses: %s [%04X] version %d.%d", prodnm, hello2.product, majver, minver);
Syslog('+', " system: %s",(char*)hello2.my_name);
strncpy(history.system_name, (char *)hello2.my_name, 35);
Syslog('+', " sysop: %s",(char*)hello2.sysop);
strncpy(history.sysop, (char *)hello2.sysop, 35);
snprintf(history.location, 10, "Somewhere");
free(prodnm);
return loaded;
}
Hello gethello2(unsigned char Hellop[])
{
int i;
Hello p;
p.signal=Hellop[0]+(Hellop[1]<<8);
p.hello_version=Hellop[2]+(Hellop[3]<<8);
p.product=Hellop[4]+(Hellop[5]<<8);
p.product_maj=Hellop[6]+(Hellop[7]<<8);
p.product_min=Hellop[8]+(Hellop[9]<<8);
for (i=0;i<60;i++)
p.my_name[i]=Hellop[10+i];
for (i=0;i<20;i++)
p.sysop[i]=Hellop[70+i];
p.my_zone=Hellop[90]+(Hellop[91]<<8);
p.my_net=Hellop[92]+(Hellop[93]<<8);
p.my_node=Hellop[94]+(Hellop[95]<<8);
p.my_point=Hellop[96]+(Hellop[97]<<8);
for (i=0;i<8;i++)
p.my_password[i]=Hellop[98+i];
for (i=0;i<8;i++)
p.reserved2[i]=Hellop[106+i];
p.capabilities=Hellop[114]+(Hellop[115]<<8);
for (i=0;i<12;i++)
p.reserved3[i]=Hellop[116+i];
return p;
}