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/session.c
2015-11-05 18:49:27 -05:00

760 lines
18 KiB
C

/*****************************************************************************
*
* Purpose ...............: Fidonet mailer
*
*****************************************************************************
* Copyright (C) 1997-2011
*
* 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.
*****************************************************************************/
#include "../config.h"
#include "../lib/mbselib.h"
#include "../lib/nodelist.h"
#include "ttyio.h"
#include "statetbl.h"
#include "emsi.h"
#include "ftsc.h"
#include "session.h"
#include "yoohoo.h"
#include "mbcico.h"
#include "binkp.h"
#include "callstat.h"
#include "inbound.h"
#include "opentcp.h"
#include "telnet.h"
extern int tcp_mode;
extern pid_t mypid;
node *nlent = NULL;
fa_list *remote = NULL;
int session_flags;
int remote_flags;
int laststat = 0; /* Last session status with remote */
int tx_define_type(void);
int rx_define_type(void);
int session_type = SESSION_UNKNOWN;
int session_state = STATE_BAD;
static char *data=NULL;
struct sockaddr_in peeraddr4;
struct sockaddr_in6 peeraddr6;
char *typestr(int);
char *typestr(int tp)
{
switch (tp) {
case SESSION_FTSC: return (char *)"FTS-0001";
case SESSION_YOOHOO: return (char *)"YooHoo/2U2";
case SESSION_EMSI: return (char *)"EMSI";
case SESSION_BINKP: return (char *)"Binkp";
default: return (char *)"Unknown";
}
}
#ifdef HAVE_GEOIP_H
extern void _GeoIP_setup_dbfilename(void);
void geoiplookup(GeoIP* gi, char *hostname, int i)
{
const char * country_code;
const char * country_name;
const char * country_continent;
int country_id;
if (GEOIP_COUNTRY_EDITION == i) {
country_id = GeoIP_id_by_name(gi, hostname);
country_code = GeoIP_country_code[country_id];
country_name = GeoIP_country_name[country_id];
country_continent = GeoIP_country_continent[country_id];
if (country_code == NULL) {
Syslog('+', "%s: IP Address not found\n", GeoIPDBDescription[i]);
} else {
Syslog('+', "GeoIP location: %s, %s %s\n", country_name, country_code, country_continent);
}
}
if (GEOIP_COUNTRY_EDITION_V6 == i) {
country_id = GeoIP_id_by_name_v6(gi, hostname);
country_code = GeoIP_country_code[country_id];
country_name = GeoIP_country_name[country_id];
country_continent = GeoIP_country_continent[country_id];
if (country_code == NULL) {
Syslog('+', "%s: IP Address not found\n", GeoIPDBDescription[i]);
} else {
Syslog('+', "GeoIP location: %s, %s %s\n", country_name, country_code, country_continent);
}
}
}
#endif
int session(faddr *a, node *nl, int role, int tp, char *dt)
{
int rc = MBERR_OK;
socklen_t addrlen = sizeof(struct sockaddr_in6);
fa_list *tmpl;
int Fdo = -1, input_pipe[2], output_pipe[2];
pid_t ipid, opid;
char str[INET6_ADDRSTRLEN];
#ifdef HAVE_GEOIP_H
GeoIP *gi;
#endif
session_flags = 0;
session_type = tp;
nlent = nl;
if (getpeername(0,(struct sockaddr *)&peeraddr6, &addrlen) == 0) {
/*
* Copy IPv4 part into the IPv6 structure. There has to be a better way
* to deal with mixed incoming sockets ???
*/
memcpy(&peeraddr4, &peeraddr6, sizeof(struct sockaddr_in));
if ((peeraddr6.sin6_family == AF_INET6) && (inet_ntop(AF_INET6, &peeraddr6.sin6_addr, str, sizeof(str)))) {
Syslog('s', "IPv6 TCP connection: len=%d, port=%hu, addr=%s", addrlen, ntohs(peeraddr6.sin6_port), str);
} else if ((peeraddr4.sin_family == AF_INET) && (inet_ntop(AF_INET, &peeraddr4.sin_addr, str, sizeof(str)))) {
Syslog('s', "IPv4 TCP connection: len=%d, port=%hu, addr=%s", addrlen, ntohs(peeraddr4.sin_port), str);
}
if (role == 0) {
if (tcp_mode == TCPMODE_IBN) {
Syslog('+', "Incoming IBN/TCP connection from %s", str);
IsDoing("Incoming IBN/TCP");
} else if (tcp_mode == TCPMODE_IFC) {
Syslog('+', "Incoming IFC/TCP connection from %s", str);
IsDoing("Incoming IFC/TCP");
} else if (tcp_mode == TCPMODE_ITN) {
Syslog('+', "Incoming ITN/TCP connection from %s", str);
IsDoing("Incoming ITN/TCP");
} else if (tcp_mode == TCPMODE_NONE) {
WriteError("Unknown TCP connection, parameter missing");
die(MBERR_COMMANDLINE);
}
}
session_flags |= SESSION_TCP;
#ifdef HAVE_GEOIP_H
_GeoIP_setup_dbfilename();
if (peeraddr6.sin6_family == AF_INET6) {
if (GeoIP_db_avail(GEOIP_COUNTRY_EDITION_V6)) {
if ((gi = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD)) != NULL) {
geoiplookup(gi, str, GEOIP_COUNTRY_EDITION_V6);
}
GeoIP_delete(gi);
}
} else if (peeraddr6.sin6_family == AF_INET) {
if (GeoIP_db_avail(GEOIP_COUNTRY_EDITION)) {
if ((gi = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD)) != NULL) {
geoiplookup(gi, str, GEOIP_COUNTRY_EDITION);
}
GeoIP_delete(gi);
}
}
#endif
if (tcp_mode == TCPMODE_ITN) {
Syslog('s', "Installing telnet filter...");
/*
* First make sure the current input socket gets a new file descriptor
* since it's now on stadin and stdout.
*/
Fdo = dup(0);
/*
* Close stdin and stdout so that when we create the pipes to
* the telnet filter they get stdin and stdout as file descriptors.
*/
fflush(stdin);
fflush(stdout);
setbuf(stdin,NULL);
setbuf(stdout, NULL);
close(0);
close(1);
/*
* Create output pipe and start output filter.
*/
if (pipe(output_pipe) == -1) {
WriteError("$could not create output_pipe");
die(MBERR_TTYIO_ERROR);
}
opid = fork();
switch (opid) {
case -1: WriteError("fork for telout_filter failed");
die(MBERR_TTYIO_ERROR);
case 0: if (close(output_pipe[1]) == -1) {
WriteError("$error close output_pipe[1]");
die(MBERR_TTYIO_ERROR);
}
telout_filter(output_pipe[0], Fdo);
/* NOT REACHED */
}
if (close(output_pipe[0] == -1)) {
WriteError("$error close output_pipe[0]");
die(MBERR_TTYIO_ERROR);
}
Syslog('s', "telout_filter forked with pid %d", opid);
/*
* Create input pipe and start input filter
*/
if (pipe(input_pipe) == -1) {
WriteError("$could not create input_pipe");
die(MBERR_TTYIO_ERROR);
}
ipid = fork();
switch (ipid) {
case -1: WriteError("fork for telin_filter failed");
die(MBERR_TTYIO_ERROR);
case 0: if (close(input_pipe[0]) == -1) {
WriteError("$error close input_pipe[0]");
die(MBERR_TTYIO_ERROR);
}
telin_filter(input_pipe[1], Fdo);
/* NOT REACHED */
}
if (close(input_pipe[1]) == -1) {
WriteError("$error close input_pipe[1]");
die(MBERR_TTYIO_ERROR);
}
Syslog('s', "telin_filter forked with pid %d", ipid);
Syslog('s', "stdout = %d", output_pipe[1]);
Syslog('s', "stdin = %d", input_pipe[0]);
if ((input_pipe[0] != 0) || (output_pipe[1] != 1)) {
WriteError("Failed to create pipes on stdin and stdout");
die(MBERR_TTYIO_ERROR);
}
Syslog('+', "Telnet I/O filters installed");
telnet_init(Fdo);
}
}
if (data)
free(data);
data=NULL;
if (dt)
data=xstrcpy(dt);
emsi_local_protos=0;
emsi_local_opts=0;
emsi_local_lcodes=0;
tidy_falist(&remote);
remote=NULL;
if (a) {
remote=(fa_list*)malloc(sizeof(fa_list));
remote->next=NULL;
remote->addr=(faddr*)malloc(sizeof(faddr));
remote->addr->zone=a->zone;
remote->addr->net=a->net;
remote->addr->node=a->node;
remote->addr->point=a->point;
remote->addr->domain=xstrcpy(a->domain);
remote->addr->name=NULL;
} else {
remote=NULL;
}
remote_flags=SESSION_FNC;
if (role) {
if (session_type == SESSION_UNKNOWN)
(void)tx_define_type();
Syslog('+', "Start outbound %s session with %s", typestr(session_type), ascfnode(a,0x1f));
switch(session_type) {
case SESSION_UNKNOWN: rc = MBERR_UNKNOWN_SESSION; break;
case SESSION_FTSC: rc = tx_ftsc(); break;
case SESSION_YOOHOO: rc = tx_yoohoo(); break;
case SESSION_EMSI: rc = tx_emsi(data); break;
case SESSION_BINKP: rc = binkp(role); break;
}
} else {
if (session_type == SESSION_FTSC)
session_flags |= FTSC_XMODEM_CRC;
if (session_type == SESSION_UNKNOWN)
(void)rx_define_type();
Syslog('+', "Start inbound %s session", typestr(session_type));
switch(session_type) {
case SESSION_UNKNOWN: rc = MBERR_UNKNOWN_SESSION; break;
case SESSION_FTSC: rc = rx_ftsc(); break;
case SESSION_YOOHOO: rc = rx_yoohoo(); break;
case SESSION_EMSI: rc = rx_emsi(data); break;
case SESSION_BINKP: rc = binkp(role); break;
}
}
sleep(2);
for (tmpl = remote; tmpl; tmpl = tmpl->next) {
/*
* Unlock all nodes, locks not owned by us are untouched.
*/
(void)nodeulock(tmpl->addr, mypid);
/*
* If successfull session, reset all status records.
*/
if (rc == 0)
putstatus(tmpl->addr, 0, 0);
}
if (rc)
session_state = STATE_BAD;
/*
* If the socket for the telnet filter is open, close it so that the telnet filters exit.
* After that wait a little while to let the filter childs die before the main program
* does, else we get zombies.
*/
if (Fdo != -1) {
Syslog('s', "shutdown filter sockets and stdio");
shutdown(Fdo, 2);
close(0);
close(1);
close(output_pipe[1]);
close(input_pipe[0]);
msleep(100);
}
tidy_falist(&remote);
if (data)
free(data);
data = NULL;
if (emsi_local_password)
free(emsi_local_password);
if (emsi_remote_password)
free(emsi_remote_password);
inbound_close(rc == 0);
return rc;
}
SM_DECL(tx_define_type,(char *)"tx_define_type")
SM_STATES
skipjunk,
wake,
waitchar,
nextchar,
checkintro,
sendinq
SM_NAMES
(char *)"skipjunk",
(char *)"wake",
(char *)"waitchar",
(char *)"nextchar",
(char *)"checkintro",
(char *)"sendinq"
SM_EDECL
int c = 0;
char buf[256], *p;
char ebuf[13], *ep;
int standby = 0;
int maybeftsc=0;
int maybeyoohoo=0;
session_type = SESSION_UNKNOWN;
ebuf[0] = '\0';
ep = ebuf;
buf[0] = '\0';
p = buf;
SM_START(skipjunk)
SM_STATE(skipjunk)
while ((c = GETCHAR(1)) >= 0) /*nothing*/ ;
if (c == TIMEOUT) {
gpt_resettimers();
gpt_settimer(0, 60); /* 60 second master timer */
SM_PROCEED(wake);
} else {
SM_ERROR;
}
SM_STATE(wake)
if (gpt_expired(0)) {
Syslog('+', "Remote did not respond");
SM_ERROR;
}
p = buf;
PUTCHAR('\r');
if ((c = GETCHAR(2)) == TIMEOUT) {
SM_PROCEED(wake);
} else if (c < 0) {
WriteError("Error while waking remote");
SM_ERROR;
} else {
gpt_settimer(0, 60);
SM_PROCEED(nextchar);
}
SM_STATE(waitchar)
if ((c = GETCHAR(2)) == TIMEOUT) {
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if (gpt_expired(0)) {
Syslog('+', "Too many tries waking remote");
SM_ERROR;
}
SM_PROCEED(sendinq);
} else if (c < 0) {
Syslog('+', "Error while getting intro from remote");
SM_ERROR;
} else {
SM_PROCEED(nextchar);
}
SM_STATE(nextchar)
if (c == 'C') {
session_flags |= FTSC_XMODEM_CRC;
maybeftsc++;
}
if (c == NAK) {
session_flags &= ~FTSC_XMODEM_CRC;
maybeftsc++;
}
if (c == ENQ)
maybeyoohoo++;
if (((localoptions & NOWAZOO) == 0) && (maybeyoohoo > 1)) {
session_type = SESSION_YOOHOO;
SM_SUCCESS;
}
if (maybeftsc > 1) {
session_type = SESSION_FTSC;
SM_SUCCESS;
}
if ((c >= ' ') && (c <= '~')) {
if (c != 'C')
maybeftsc = 0;
maybeyoohoo = 0;
if ((p-buf) < (sizeof(buf)-1)) {
*p++ = c;
*p = '\0';
}
if (c == '*') {
standby = 1;
ep = ebuf;
buf[0] = '\0';
} else if (standby) {
if ((ep - ebuf) < (sizeof(ebuf) - 1)) {
*ep++ = c;
*ep = '\0';
}
if ((ep - ebuf) >= (sizeof(ebuf) - 1)) {
standby = 0;
SM_PROCEED(checkintro);
}
}
} else {
switch (c) {
case DC1: break;
case '\r':
case '\n': standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if (buf[0])
Syslog('+', "Intro: \"%s\"", printable(buf, 0));
p = buf;
buf[0] = '\0';
break;
default: standby = 0;
ep = ebuf;
ebuf[0] = '\0';
Syslog('i', "Got '%s' reading intro", printablec(c));
break;
}
}
SM_PROCEED(waitchar);
SM_STATE(checkintro)
Syslog('i', "Check \"%s\" for being EMSI request",ebuf);
if (((localoptions & NOEMSI) == 0) && (strncasecmp(ebuf,"EMSI_REQA77E",12) == 0)) {
session_type = SESSION_EMSI;
data = xstrcpy((char *)"**EMSI_REQA77E");
Syslog('i', "Sending **EMSI_INQC816 (2 times)");
PUTSTR((char *)"\r**EMSI_INQC816\r**EMSI_INQC816\r\021");
SM_SUCCESS;
} else {
p = buf;
buf[0] = '\0';
SM_PROCEED(waitchar);
}
SM_STATE(sendinq)
PUTCHAR(DC1);
if ((localoptions & NOEMSI) == 0) {
PUTSTR((char *)"\r**EMSI_INQC816\r**EMSI_INQC816");
}
if ((localoptions & NOWAZOO) == 0) {
PUTCHAR(YOOHOO);
}
PUTCHAR(TSYNC);
if ((localoptions & NOEMSI) == 0)
PUTSTR((char *)"\r\021");
SM_PROCEED(waitchar);
SM_END
SM_RETURN
SM_DECL(rx_define_type,(char *)"rx_define_type")
SM_STATES
sendintro,
settimer,
waitchar,
nextchar,
checkemsi,
getdat
SM_NAMES
(char *)"sendintro",
(char *)"settimer",
(char *)"waitchar",
(char *)"nextchar",
(char *)"checkemsi",
(char *)"getdat"
SM_EDECL
int count=0;
int c=0;
int maybeftsc=0,maybeyoohoo=0;
char ebuf[13],*ep;
char *p;
int standby=0;
int datasize;
session_type=SESSION_UNKNOWN;
session_flags|=FTSC_XMODEM_CRC;
ebuf[0]='\0';
ep=ebuf;
gpt_resettimers();
gpt_settimer(0, 60);
gpt_settimer(1, 20);
SM_START(sendintro)
SM_STATE(sendintro)
if (count++ > 6) {
Syslog('+', "Too many tries to get anything from the caller");
SM_ERROR;
}
Syslog('s', "rxdefine_type SENDINTRO count=%d", count);
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if ((localoptions & NOEMSI) == 0) {
PUTSTR((char *)"**EMSI_REQA77E\r\021");
}
PUTCHAR('\r');
if (STATUS) {
SM_ERROR;
} else {
SM_PROCEED(settimer);
}
SM_STATE(settimer)
Syslog('s', "Set 20 secs timer");
gpt_settimer(1, 20);
SM_PROCEED(waitchar);
SM_STATE(waitchar)
if (gpt_expired(0)) {
Syslog('+', "Session setup timeout");
SM_ERROR;
}
if (gpt_expired(1)) {
Syslog('s', "20 sec timer timeout");
SM_PROCEED(sendintro);
}
if ((c = GETCHAR(1)) == TIMEOUT) {
SM_PROCEED(waitchar);
} else if (c < 0) {
Syslog('+', "Session setup error");
SM_ERROR;
} else {
SM_PROCEED(nextchar);
}
SM_STATE(nextchar)
if ((c >= ' ') && (c <= 'z')) {
if (c == '*') {
standby = 1;
ep = ebuf;
ebuf[0] = '\0';
} else if (standby) {
if ((ep - ebuf) < (sizeof(ebuf) - 1)) {
*ep++ = c;
*ep = '\0';
}
if ((ep - ebuf) >= (sizeof(ebuf) - 1)) {
standby = 0;
SM_PROCEED(checkemsi);
}
}
SM_PROCEED(waitchar);
} else {
switch (c) {
case DC1: SM_PROCEED(waitchar);
break;
case TSYNC: standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if (++maybeftsc > 1) {
session_type = SESSION_FTSC;
SM_SUCCESS;
} else {
SM_PROCEED(waitchar);
}
break;
case YOOHOO:standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if (++maybeyoohoo > 1) {
session_type = SESSION_YOOHOO;
SM_SUCCESS;
} else {
SM_PROCEED(waitchar);
}
break;
case '\r':
case '\n': standby = 0;
ep = ebuf;
ebuf[0] = '\0';
if (ebuf[0]) {
SM_PROCEED(checkemsi);
} else {
/*
* If the 20 second timer is expired or after the
* first sendintro, send the intro again. After
* that take it easy.
*/
if (gpt_expired(1) || (count == 1)) {
Syslog('s', "sendintro after eol char");
SM_PROCEED(sendintro);
} else {
Syslog('s', "waitchar after eol char");
SM_PROCEED(waitchar);
}
}
break;
default: standby = 0;
ep = ebuf;
ebuf[0] = '\0';
Syslog('i', "Got '%s' from remote", printablec(c));
SM_PROCEED(waitchar);
break;
}
}
SM_STATE(checkemsi)
Syslog('i', "check \"%s\" for being EMSI inquery or data",ebuf);
if (localoptions & NOEMSI) {
Syslog('s', "Force sendintro");
SM_PROCEED(sendintro);
}
if (strncasecmp(ebuf, "EMSI_INQC816", 12) == 0) {
session_type = SESSION_EMSI;
data = xstrcpy((char *)"**EMSI_INQC816");
SM_SUCCESS;
} else if (strncasecmp(ebuf, "EMSI_HBT", 8) == 0) {
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
SM_PROCEED(settimer);
} else if (strncasecmp(ebuf, "EMSI_DAT", 8) == 0) {
SM_PROCEED(getdat);
} else {
SM_PROCEED(settimer);
}
SM_STATE(getdat)
Syslog('i', "Try get emsi_dat packet starting with \"%s\"",ebuf);
if (sscanf(ebuf+8, "%04x", &datasize) != 1) {
SM_PROCEED(sendintro);
}
datasize += 18; /* strlen("**EMSI_DATxxxxyyyy"), include CRC */
data=malloc(datasize+1);
strcpy(data,"**");
strcat(data, ebuf);
p = data + strlen(data);
while (((p - data) < datasize) && ((c = GETCHAR(8)) >= 0)) {
*p++ = c;
*p= '\0';
}
if (c == TIMEOUT) {
Syslog('s', "c = TIMEOUT -> sendintro");
SM_PROCEED(sendintro);
} else if (c < 0) {
Syslog('+', "Error while reading EMSI_DAT from the caller");
SM_ERROR;
}
session_type = SESSION_EMSI;
SM_SUCCESS;
SM_END
SM_RETURN