/***************************************************************************** * * $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 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 *)""); 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; }