bforce/source/bforce/io_tcpip.c
Alexey Khromov 2b63aa8cd7
All checks were successful
Archlinux build / build-arch (push) Successful in 3m37s
Debian build / build-ubuntu (push) Successful in 3m39s
Some minor lint fixes from PVS-Studio, added Alt workflow
2025-04-15 23:31:40 +03:00

302 lines
6.3 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"
#define DEFAULT_PORT 60179 /* Birthday .. mother fucker :) */
struct addrinfo *paddrinfo = NULL;
static RETSIGTYPE tcpip_interrupt(int sig)
{
tty_abort = TRUE;
(void)log("terminating on signal %d", sig);
}
static RETSIGTYPE tcpip_brokenpipe(int sig)
{
tty_abort = TRUE;
if( tty_online )
(void)log("connection closed");
else
(void)log("terminating on signal %d", sig);
}
static int tcpip_connect2(struct addrinfo *ai)
{
int fd = -1;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
struct addrinfo *rai;
for (rai = ai; rai != NULL; rai = rai->ai_next)
{
if (getnameinfo(rai->ai_addr, rai->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0)
DEB((D_INFO, "tcpip_connect2: trying host=%s, serv=%s", hbuf, sbuf));
fd = socket (rai->ai_family, rai->ai_socktype, rai->ai_protocol);
if ( fd == -1 )
{
DEB((D_INFO, "tcpip_connect2: socket error"));
}
else break;
hbuf[0]='\0';
sbuf[0]='\0';
}
DEB((D_INFO, "tcpip_connect2: socket success: %d", fd));
if (fd < 0 )
{
logerr("can't create socket");
return(1);
}
/* make new fd == stdin if it isn't already */
if( fd > 0 )
{
(void)close(0);
if( dup(fd) != 0 )
{
logerr("cannot dup socket to stdin");
return(1);
}
}
/* make stdout and stderr, too */
(void)close(1);
(void)close(2);
if( dup(0) != 1 )
{
logerr("cannot dup stdin to stdout");
return(1);
}
if( dup(0) != 2 )
{
logerr("cannot dup stdin to stderr");
return(1);
}
if( fd > 2 ) (void)close(fd);
/* switch off stdio buffering */
setbuf(stdin, (char *)NULL);
setbuf(stdout, (char *)NULL);
setbuf(stderr, (char *)NULL);
clearerr(stdin);
clearerr(stdout);
clearerr(stderr);
if( connect(0, rai->ai_addr, rai->ai_addrlen) == -1 )
{
logerr("can't connect to %s", hbuf);
close(0);
close(1);
close(2);
return 1;
}
if( tcpip_init() )
{
tcpip_shutdown();
return 1;
}
(void)log("TCP/IP connect success to %s on service %s", hbuf, sbuf);
return(0);
}
int tcpip_connect(const char *hostname, e_tcpmode tcpmode)
{
int rc = 0;
//struct hostent *he = NULL;
//struct servent *se = NULL;
//struct sockaddr_in server;
//struct sockaddr_storage server;
struct addrinfo *aisave = NULL;
struct addrinfo aihints;
int nameres = 0;
char *host = xstrcpy(hostname);
char *p = strrchr(host, ':');
const char *port = NULL;
// AF_INET - only IPv4
//server.sin_family = AF_INET;
if( p )
{ *p++ = '\0'; port = p; }
else if( tcpmode == TCPMODE_BINKP )
port = "binkp";
else if( tcpmode == TCPMODE_TELNET )
port = "telnet";
else /* Can not rely on /etc/services - it is not well-known port */
port = "60179";
// Do not need it
/*
if( ISDEC(port) )
server.sin_port = htons(atoi(port));
else if( (se = getservbyname(port, "tcp")) )
server.sin_port = se->s_port;
else
{ log("invalid port or service name \"%s\"", port); rc = 1; }
*/
//if( rc == 0 )
//{
DEB((D_INFO, "tcpip_connect: port is %s", port));
memset( &aihints, 0, sizeof( aihints) );
aihints.ai_family = AF_UNSPEC;
//aihints.ai_socktype = SOCK_STREAM;
aihints.ai_protocol = IPPROTO_TCP;
nameres = getaddrinfo ( host, port, &aihints, &paddrinfo );
if ( nameres != 0 )
{
rc = 1;
log("Resolver error for host %s, port %s: \"%s\".", host, port, gai_strerror(nameres));
}
// GetHostByName works with IPv4 only and *deprecated*
/*
if( (he = gethostbyname(host)) )
{
memcpy(&server.sin_addr, he->h_addr, he->h_length);
}
else
{
rc = 1;
switch(h_errno) {
case HOST_NOT_FOUND:
log("host \"%s\" not found", host);
break;
case NO_ADDRESS:
log("no IP address found for host \"%s\"", host);
break;
case NO_RECOVERY:
log("non-recoverable name server error occured");
break;
case TRY_AGAIN:
log("temporary error occured on name server");
break;
default:
log("unknown error while resolving host \"%s\"", host);
break;
}
} */
//}
if( host ) { free(host); host = NULL; }
DEB((D_INFO, "tcpip_connect: resolver got result!"));
aisave = paddrinfo;
if (rc == 0 )
rc = tcpip_connect2(paddrinfo);
freeaddrinfo( aisave );
return rc;
}
int tcpip_init(void)
{
int nbio_arg = 1;
int alive_arg = 1;
struct linger lingeropt;
tty_online = TRUE;
tty_abort = FALSE;
tty_hangup = FALSE;
tty_modem = FALSE;
/*
* Set sockets I/O to the non-blocking mode
*/
if( ioctl(0, FIONBIO, (char *)&nbio_arg, sizeof(nbio_arg)) == -1 )
{
logerr("failed to set non-blocking mode for stdin");
return 1;
}
if( ioctl(1, FIONBIO, (char *)&nbio_arg, sizeof(nbio_arg)) == -1 )
{
logerr("failed to set non-blocking mode for stdout");
return 1;
}
/*
* Set SO_LONGER socket option, so then we will close
* socket the system will block our close() call and
* try to deliver queued data.
*/
memset(&lingeropt, '\0', sizeof(struct linger));
lingeropt.l_onoff = 1;
lingeropt.l_linger = 5*100; /* 5 seconds */
if( setsockopt(1, SOL_SOCKET, SO_LINGER, (char*)&lingeropt, sizeof(struct linger)) == -1 )
{
logerr("failed to set SO_LINGER socket option for the stdout");
return 1;
}
/*
* Set SO_KEEPALIVE socket option. The connection will be
* considered broken if remote side fail to respond on
* periodicaly transmitted message. We should not hang
* even without such features.
*/
if( setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char*)&alive_arg, sizeof(alive_arg)) == -1 )
{
logerr("failed to set SO_KEEPALIVE socket option");
return 1;
}
signal(SIGHUP, tcpip_interrupt);
signal(SIGINT, tcpip_interrupt);
signal(SIGTERM, tcpip_interrupt);
signal(SIGPIPE, tcpip_brokenpipe);
return(0);
}
int tcpip_shutdown(void)
{
close(0);
close(1);
close(2);
return 0;
}
bool tcpip_isgood_host(const char *str)
{
if( !str || !str[0] )
return FALSE;
if( str[0] == '-' && str[1] == '\0' )
return FALSE;
return TRUE;
}