src/netconf.c
/*
MODULE -- network configuration module (RNC)
Copyright (C) Alberto Ornaghi
$Id: netconf.c 3558 2011-06-07 10:59:30Z alor $
*/
#include <main.h>
#include <file.h>
#include <netconf.h>
#include <threads.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <netdb.h>
#include <signal.h>
#include <sys/statvfs.h>
/* globals */
/* protos */
void netconf_start(void);
MY_THREAD_FUNC(rnc_communicator);
//static int tcp_connect(char *host, int port);
//static int tcp_accept(int sock);
int ssl_proto_read(BIO *ssl, void *buf, int num);
int ssl_proto_write(BIO *ssl, void *buf, int num);
void rnc_handleproto(BIO *ssl);
int rnc_sendversion(BIO *ssl);
int rnc_sendmonitor(BIO *ssl, char *status, char *desc);
int rnc_retrieveconf(BIO *ssl);
int rnc_sendlogs(BIO *ssl);
void get_system_stats(u_int *disk, u_int *cpu, u_int *pcpu);
/************************************************/
void netconf_start(void)
{
/* check when to not initialize the proxy */
if (GBL_OPTIONS->read) {
DEBUG_MSG(D_INFO, "netconf_start: skipping... (reading offline)");
return;
}
my_thread_new("netconf", "RNC communication module", &rnc_communicator, NULL);
}
MY_THREAD_FUNC(rnc_communicator)
{
SSL_CTX *ctx;
SSL *ssl;
BIO *sbio, *abio, *cbio;
char *certfile;
char listen_port[32];
/* initialize the thread */
my_thread_init();
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
/* create the SSL stuff */
ctx = SSL_CTX_new(SSLv23_server_method());
certfile = get_path("etc", "ca.pem");
if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) == 0)
ERROR_MSG("Cannot load the certificate from %s", certfile);
if (SSL_CTX_use_PrivateKey_file(ctx, certfile, SSL_FILETYPE_PEM) <= 0)
ERROR_MSG("Cannot load the private key from %s", certfile);
if (SSL_CTX_check_private_key(ctx) <= 0)
ERROR_MSG("Cannot invalid private key from %s", certfile);
SAFE_FREE(certfile);
DEBUG_MSG(D_DEBUG, "SSL_CTX initialized");
/* New SSL BIO setup as server */
sbio = BIO_new_ssl(ctx, 0);
BIO_get_ssl(sbio, &ssl);
if (!ssl)
ERROR_MSG("Cannot inizialize SSL");
/* Don't want any retries */
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* listen on port */
snprintf(listen_port, sizeof(listen_port), "0.0.0.0:%d", GBL_NETCONF->rnc_port);
abio = BIO_new_accept(listen_port);
/* reuse the address */
BIO_set_bind_mode(abio, BIO_BIND_REUSEADDR);
BIO_set_accept_bios(abio, sbio);
/* First call to BIO_accept() sets up accept BIO */
if (BIO_do_accept(abio) <= 0)
ERROR_MSG("Cannot bind port %d for RNC communication", GBL_NETCONF->rnc_port);
else
DEBUG_MSG(D_INFO, "Server listening on port %d", GBL_NETCONF->rnc_port);
/* main loop waiting to be contacted by RNC */
LOOP {
/* Wait for incoming connection */
if (BIO_do_accept(abio) <= 0) {
DEBUG_MSG(D_ERROR, "Cannot perform BIO_do_accept");
continue;
}
/* get the connected client */
cbio = BIO_pop(abio);
if (BIO_do_handshake(cbio) <= 0) {
DEBUG_MSG(D_ERROR, "Cannot handshake SSL");
continue;
}
/* handle the communication with the RNC */
rnc_handleproto(cbio);
/* close the connection */
BIO_ssl_shutdown(cbio);
(void) BIO_flush(cbio);
BIO_free_all(cbio);
DEBUG_MSG(D_DEBUG, "Closing connection");
}
SSL_CTX_free(ctx);
BIO_free(abio);
BIO_free(sbio);
/* NEVER REACHED */
return NULL;
}
#if 0
static int tcp_connect(char *host, int port)
{
struct hostent *hp;
struct sockaddr_in addr;
int sock;
if ( !(hp = gethostbyname(host)) ) {
DEBUG_MSG(D_ERROR, "Could not resolve host [%s]", host);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_addr = *(struct in_addr*)
hp->h_addr_list[0];
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if ((sock = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP)) < 0) {
DEBUG_MSG(D_ERROR, "Couldn't create socket [%s:%d]", host, port);
return -1;
}
if (connect(sock,(struct sockaddr *)&addr, sizeof(addr)) < 0) {
DEBUG_MSG(D_ERROR, "Couldn't connect socket [%s:%d]", host, port);
return -1;
}
return sock;
}
static int tcp_accept(int sock)
{
struct sockaddr_in caddr;
u_int len = sizeof(struct sockaddr);
fd_set fdread;
struct timeval tv;
int ret;
int csock;
FD_ZERO(&fdread);
FD_SET(sock, &fdread);
memset(&caddr, 0, sizeof(caddr));
/* set the timeout */
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(FOPEN_MAX, &fdread, (fd_set *)NULL, (fd_set *)NULL, NULL /*&tv*/);
if (ret == 0) {
/* timeout occurred. return false to let the main thread do other things... */
return 0;
} else if (ret == -1) {
DEBUG_MSG(D_ERROR, "tcp_accept - select socket error [%d]", errno);
return 0;
}
csock = accept(sock, (struct sockaddr *)&caddr, &len);
if (csock == -1 ) {
DEBUG_MSG(D_ERROR, "tcp_accept - invalid socket [%d]", errno);
return 0;
} else {
DEBUG_MSG(D_DEBUG, "New connection from %s", inet_ntoa(caddr.sin_addr));
return csock;
}
return 0;
}
#endif
int ssl_proto_read(BIO *ssl, void *buf, int num)
{
int read = 0;
int len;
do {
len = BIO_read(ssl, (char *)buf + read, num - read);
if (len <= 0) {
break;
}
read += len;
} while (read < num);
return read;
}
int ssl_proto_write(BIO *ssl, void *buf, int num)
{
int written = 0;
int len;
do {
len = BIO_write(ssl, (char *)buf + written, num - written);
if (len <= 0) {
break;
}
written += len;
} while (written < num);
return written;
}
void rnc_handleproto(BIO *ssl)
{
RncProtoHeader pheader;
RncProtoLogin plogin;
int ret;
char descr[1024];
DEBUG_MSG(D_DEBUG, "Handling connection from RNC");
/* read the login from RNC */
if ( (ret = ssl_proto_read(ssl, &pheader, sizeof(pheader))) <= 0) {
DEBUG_MSG(D_ERROR, "Cannot read from RNC");
return;
}
/* check if the command is correct */
if (ret < (int)sizeof(RncProtoHeader) || pheader.code != RNC_PROTO_LOGIN) {
DEBUG_MSG(D_ERROR, "Invalid login authentication");
return;
}
/* check if the signature is correct. otherwise reply with NO */
ret = ssl_proto_read(ssl, &plogin, sizeof(plogin));
if (ret < (int)sizeof(RncProtoLogin) || memcmp(plogin.sign, GBL_NETCONF->rnc_sign, RNC_SIGN_LEN)) {
DEBUG_MSG(D_ERROR, "Invalid RNC authentication [%.32s] bytes %d expected %d", plogin.sign, ret, sizeof(RncProtoLogin));
pheader.code = RNC_PROTO_NO;
pheader.size = 0;
ssl_proto_write(ssl, &pheader, sizeof(pheader));
return;
}
pheader.code = RNC_PROTO_OK;
pheader.size = 0;
if (ssl_proto_write(ssl, &pheader, sizeof(pheader)) <= 0) {
DEBUG_MSG(D_ERROR, "Cannot write to RNC");
return;
}
DEBUG_MSG(D_DEBUG, "RNC authenticated and connected");
/* send monitor status */
if (rnc_sendversion(ssl) < 0) {
DEBUG_MSG(D_ERROR, "Cannot communicate with RNC (monitor)");
return;
}
/* prepare the string for the monitor */
if (GBL_NET->network_error) {
/* send monitor status */
if (rnc_sendmonitor(ssl, "KO", "PROXY_IP is invalid, please fix the configuration...") < 0) {
DEBUG_MSG(D_ERROR, "Cannot communicate with RNC (monitor)");
return;
}
} else {
snprintf(descr,sizeof(descr), "Active users: %u of %u Redirected FQDN: %u Redirected URL: %u File Infected: %u",
(u_int)GBL_STATS->active_users,
(u_int)GBL_STATS->tot_users,
(u_int)GBL_STATS->redir_fqdn,
(u_int)GBL_STATS->redir_url,
(u_int)GBL_STATS->inf_files);
/* send monitor status */
if (rnc_sendmonitor(ssl, "OK", descr) < 0) {
DEBUG_MSG(D_ERROR, "Cannot communicate with RNC (monitor)");
return;
}
}
/* retrieve new conf (if any) */
if ((ret = rnc_retrieveconf(ssl)) < 0) {
DEBUG_MSG(D_ERROR, "Cannot communicate with RNC (config)");
return;
}
/* check if there are new configs */
if (ret) {
DEBUG_MSG(D_INFO, "Received new configuration(s), sending signal to reload them...");
/* reload the new config, the signal handler will reload them */
kill(getpid(), SIGHUP);
} else {
DEBUG_MSG(D_DEBUG, "NO new configuration this time...");
}
/* send cached logs */
if ((ret = rnc_sendlogs(ssl)) < 0) {
DEBUG_MSG(D_ERROR, "Cannot communicate with RNC (logs)");
return;
}
/* send BYE to RNC */
pheader.code = RNC_PROTO_BYE;
pheader.size = 0;
ssl_proto_write(ssl, &pheader, sizeof(pheader));
/* disconnect */
}
int rnc_sendversion(BIO *ssl)
{
RncProtoHeader pheader;
RncProtoVersion pversion;
memset(&pheader, 0, sizeof(pheader));
memset(&pversion, 0, sizeof(pversion));
/* header parameters */
pheader.code = RNC_PROTO_VERSION;
pheader.size = sizeof(pversion);
/* monitor parameters */
snprintf(pversion.version, sizeof(pversion.version), "%s", GBL_RCS_VERSION);
DEBUG_MSG(D_DEBUG, "Sending version information to RNC [%s]", GBL_RCS_VERSION);
/* send header */
if (ssl_proto_write(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
/* monitor part */
if (ssl_proto_write(ssl, &pversion, sizeof(pversion)) <= 0)
return -1;
/* read the response from RNC */
if (ssl_proto_read(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
if (pheader.code != RNC_PROTO_OK)
return -1;
return 0;
}
int rnc_sendmonitor(BIO *ssl, char *status, char *desc)
{
RncProtoHeader pheader;
RncProtoMonitor pmonitor;
memset(&pheader, 0, sizeof(pheader));
memset(&pmonitor, 0, sizeof(pmonitor));
/* header parameters */
pheader.code = RNC_PROTO_MONITOR;
pheader.size = sizeof(pmonitor);
/* monitor parameters */
snprintf(pmonitor.status, sizeof(pmonitor.status), "%s", status);
get_system_stats(&pmonitor.disk, &pmonitor.cpu, &pmonitor.pcpu);
snprintf(pmonitor.desc, sizeof(pmonitor.desc), "%s", desc);
DEBUG_MSG(D_DEBUG, "Sending monitor information to RNC [%s]", desc);
/* send header */
if (ssl_proto_write(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
/* monitor part */
if (ssl_proto_write(ssl, &pmonitor, sizeof(pmonitor)) <= 0)
return -1;
/* read the response from RNC */
if (ssl_proto_read(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
if (pheader.code != RNC_PROTO_OK)
return -1;
return 0;
}
int rnc_retrieveconf(BIO *ssl)
{
FILE *fc;
RncProtoHeader pheader;
RncProtoConfig pconfig;
int found = 0;
char *conf;
/* header parameters */
pheader.code = RNC_PROTO_CONF;
pheader.size = 0;
/* send request to check if there is new config */
if (ssl_proto_write(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
/* loop to receive the new conf */
LOOP {
memset(&pheader, 0, sizeof(pheader));
memset(&pconfig, 0, sizeof(pconfig));
/* read the response from RNC */
if (ssl_proto_read(ssl, &pheader, sizeof(pheader)) <= 0)
break;
/* there is NOT a new config */
if (pheader.code != RNC_PROTO_CONF)
break;
/* retrieve the config header */
if (ssl_proto_read(ssl, &pconfig, sizeof(pconfig)) <= 0)
break;
/* allocate the buffer and read the conf from RNC */
SAFE_CALLOC(conf, pconfig.size, sizeof(char));
if (ssl_proto_read(ssl, conf, pconfig.size) <= 0)
break;
DEBUG_MSG(D_INFO, "Received new config file [%s]", pconfig.filename);
/* open the config file for writing */
fc = open_data("etc", pconfig.filename, FOPEN_WRITE_TEXT);
ON_ERROR(fc, NULL, "Cannot open %s", pconfig.filename);
/* dump the content of the buffer received from RNC into the file */
if (fwrite(conf, sizeof(char), pconfig.size, fc) < pconfig.size)
DEBUG_MSG(D_ERROR, "Cannot write conf file [%s]", pconfig.filename);
DEBUG_MSG(D_DEBUG, "Config file [%s] written (%d bytes)", pconfig.filename, pconfig.size);
fclose(fc);
/* if the file is a ZIP archive, extract it */
if (!strcasecmp(pconfig.filename + strlen(pconfig.filename) - 4, ".zip")) {
char *path, *dir, *p;
char argv[1024];
int ret;
/* get the path of the file */
if ((path = get_path("etc", pconfig.filename)) == NULL)
continue;
dir = strdup(path);
/* trim the filename, get the dirname */
if ((p = strrchr(dir, '/')) != NULL)
*p = 0;
/* clean the vectors directory */
snprintf(argv, sizeof(argv), "/bin/rm -f %s/vectors/*", dir);
DEBUG_MSG(D_INFO, "Cleaning vectors directory...");
/* execute the command */
ret = system(argv);
if (ret == -1 || ret == 127)
DEBUG_MSG(D_ERROR, "Clean failed");
/* prepare the commandline for unzip */
snprintf(argv, sizeof(argv), "/usr/bin/unzip -o %s -d %s", path, dir);
DEBUG_MSG(D_INFO, "Uncompressing configuration file...");
/* execute the command */
ret = system(argv);
if (ret == -1 || ret == 127)
DEBUG_MSG(D_ERROR, "Unzip failed");
unlink(path);
SAFE_FREE(dir);
SAFE_FREE(path);
}
/* increment the number of received config */
found++;
}
return found;
}
int rnc_sendlogs(BIO *ssl)
{
RncProtoHeader pheader;
RncProtoLog plog;
u_int count = 0;
/* header parameters */
pheader.code = RNC_PROTO_LOG;
pheader.size = sizeof(plog);
/* send logs until there are any in the cache */
while (log_get(&plog)) {
/* send header for the log */
if (ssl_proto_write(ssl, &pheader, sizeof(pheader)) <= 0)
return -1;
/* send the log */
if (ssl_proto_write(ssl, &plog, sizeof(plog)) <= 0)
return -1;
// DEBUG_MSG(D_VERBOSE, "rnc_sendlogs - [%s]", plog.desc);
count++;
}
DEBUG_MSG(D_DEBUG, "%d log sent to RNC", count);
return count;
}
void get_system_stats(u_int *disk, u_int *cpu, u_int *pcpu)
{
FILE *fproc;
char line[1024];
int ouser, onice, osys, oidle, ohi, oirq, osoft;
int user, nice, sys, idle, hi, irq, soft;
int opuser, opsys, puser, psys;
int tot;
char *p;
struct statvfs fs;
int dummy;
char cdummy;
/* initialize the values */
*disk = -1;
*cpu = -1;
*pcpu = -1;
/* filesystem stats */
statvfs(".", &fs);
*disk = (int)((float)fs.f_bavail / (float)fs.f_blocks * 100);
memset(line, 0, sizeof(line));
/* cpu stats (globals) */
if ((fproc = fopen("/proc/stat", "r")) == NULL)
return;
dummy = fread(line, 1024 - 1, sizeof(char), fproc);
fclose(fproc);
/* get the values from the string (we need all of them) */
if (sscanf(line, "cpu %d %d %d %d %d %d %d", &ouser, &onice, &osys, &oidle, &ohi, &oirq, &osoft) != 7)
return;
memset(line, 0, sizeof(line));
/* cpu stats (current process) */
if ((fproc = fopen("/proc/self/stat", "r")) == NULL)
return;
dummy = fread(line, 1024 - 1, sizeof(char), fproc);
fclose(fproc);
/* skip the process name */
if ((p = strchr(line, ')')) == NULL)
return;
/* get the values from the string (we need only user and sys times) */
if (sscanf(p + 2, "%c %d %d %d %d %d %d %d %d %d %d %d %d",
&cdummy, &dummy, &dummy,
&dummy, &dummy, &dummy,
&dummy, &dummy, &dummy,
&dummy, &dummy,
&opuser, &opsys) != 13)
return;
/* wait 1 second for the sampling */
sleep(1);
memset(line, 0, sizeof(line));
if ((fproc = fopen("/proc/stat", "r")) == NULL)
return;
dummy = fread(line, 1024 - 1, sizeof(char), fproc);
fclose(fproc);
/* get the values from the string (we need all of them) */
if (sscanf(line, "cpu %d %d %d %d %d %d %d", &user, &nice, &sys, &idle, &hi, &irq, &soft) != 7)
return;
memset(line, 0, sizeof(line));
if ((fproc = fopen("/proc/self/stat", "r")) == NULL)
return;
dummy = fread(line, 1024 - 1, sizeof(char), fproc);
fclose(fproc);
/* skip the process name */
if ((p = strchr(line, ')')) == NULL)
return;
/* get the values from the string (we need only user and sys times) */
if (sscanf(p + 2, "%c %d %d %d %d %d %d %d %d %d %d %d %d",
&cdummy, &dummy, &dummy,
&dummy, &dummy, &dummy,
&dummy, &dummy, &dummy,
&dummy, &dummy,
&puser, &psys) != 13)
return;
tot = (user+nice+sys+idle+hi+irq+soft) - (ouser+onice+osys+oidle+ohi+oirq+osoft);
*cpu = (int)((1 - (float)(idle - oidle) / (float)tot) * 100);
*pcpu = (int)((float)(puser + psys - opuser - opsys) / (float)tot * 100);
}
/* EOF */
// vim:ts=3:expandtab