/* Chrysalide - Outil d'analyse de fichiers binaires
* controller.h - prototypes pour la gestion d'un ensemble d'archives au format CDB
*
* Copyright (C) 2021 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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 3 of the License, or
* (at your option) any later version.
*
* Chrysalide 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 Chrysalide. If not, see .
*/
#include "controller.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "backend-int.h"
#include "misc/rlestr.h"
#include "../../common/leb128.h"
#include "../../common/packed.h"
#include "../../core/logs.h"
/* Description d'un contrôleur d'archives (instance) */
struct _GCdbController
{
GServerBackend parent; /* A laisser en premier */
char *basedir; /* Répertoire du serveur */
SSL *tls_fd; /* Canal de communication */
char *peer_name; /* Désignation du correspondant*/
char *user; /* Utilisateur connecté */
};
/* Description d'un contrôleur d'archives (classe) */
struct _GCdbControllerClass
{
GServerBackendClass parent; /* A laisser en premier */
};
/* Initialise la classe des contrôleurs d'archives. */
static void g_cdb_controller_class_init(GCdbControllerClass *);
/* Initialise un contrôleur d'archives. */
static void g_cdb_controller_init(GCdbController *);
/* Supprime toutes les références externes. */
static void g_cdb_controller_dispose(GCdbController *);
/* Procède à la libération totale de la mémoire. */
static void g_cdb_controller_finalize(GCdbController *);
/* Assure le traitement des requêtes de contrôle. */
static void *g_cdb_controller_process(GCdbController *);
/* Prend en compte une connexion nouvelle d'un utilisateur. */
static void g_cdb_controller_add_client(GCdbController *, SSL *, const char *, const char *);
/* Envoie au client la liste des binaires présents. */
static bool g_cdb_controller_send_existing_binaries(GCdbController *, packed_buffer_t *);
/* Indique le type défini pour une gestion d'archives. */
G_DEFINE_TYPE(GCdbController, g_cdb_controller, G_TYPE_SERVER_BACKEND);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des contrôleurs d'archives. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_cdb_controller_class_init(GCdbControllerClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GServerBackendClass *backend; /* Classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_cdb_controller_dispose;
object->finalize = (GObjectFinalizeFunc)g_cdb_controller_finalize;
backend = G_SERVER_BACKEND_CLASS(klass);
backend->thread_name = "cdb_controller";
backend->thread_func = (GThreadFunc)g_cdb_controller_process;
backend->add_client = (add_backend_client_fc)g_cdb_controller_add_client;
}
/******************************************************************************
* *
* Paramètres : controller = instance à initialiser. *
* *
* Description : Initialise un contrôleur d'archives. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_cdb_controller_init(GCdbController *controller)
{
controller->basedir = NULL;
controller->tls_fd = NULL;
controller->peer_name = NULL;
controller->user = NULL;
}
/******************************************************************************
* *
* Paramètres : controller = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_cdb_controller_dispose(GCdbController *controller)
{
g_server_backend_stop(G_SERVER_BACKEND(controller));
G_OBJECT_CLASS(g_cdb_controller_parent_class)->dispose(G_OBJECT(controller));
}
/******************************************************************************
* *
* Paramètres : controller = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_cdb_controller_finalize(GCdbController *controller)
{
if (controller->basedir != NULL)
free(controller->basedir);
if (controller->tls_fd != NULL)
SSL_free(controller->tls_fd);
if (controller->peer_name != NULL)
free(controller->peer_name);
if (controller->user != NULL)
free(controller->user);
G_OBJECT_CLASS(g_cdb_controller_parent_class)->finalize(G_OBJECT(controller));
}
/******************************************************************************
* *
* Paramètres : basedir = répertoire de stockage des enregistrements. *
* error = indication éventuelle en cas d'échec. [OUT] *
* *
* Description : Définit ou ouvre une archive d'éléments utilisateur. *
* *
* Retour : Structure mise en plae ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GCdbController *g_cdb_controller_new(const char *basedir, DBError *error)
{
GCdbController *result; /* Adresse à retourner */
result = g_object_new(G_TYPE_CDB_CONTROLLER, NULL);
result->basedir = strdup(basedir);
*error = DBE_NONE;
return result;
}
/******************************************************************************
* *
* Paramètres : controller = centralisation de tous les savoirs. *
* *
* Description : Assure le traitement des requêtes de contrôle. *
* *
* Retour : NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
static void *g_cdb_controller_process(GCdbController *controller)
{
GServerBackend *base; /* Base de l'instance */
struct pollfd fds[3]; /* Surveillance des flux */
int ret; /* Bilan d'un appel */
packed_buffer_t in_pbuf; /* Tampon de réception */
bool status; /* Bilan de lecture initiale */
uint32_t command; /* Commande de la requête */
packed_buffer_t out_pbuf; /* Tampon d'émission */
char *msg; /* Erreur à faire remonter */
base = G_SERVER_BACKEND(controller);
fds[0].fd = base->stop_ctrl[0];
fds[0].events = POLLIN | POLLPRI;
fds[1].fd = base->refresh_ctrl[0];
fds[1].events = POLLIN | POLLPRI;
fds[2].fd = SSL_get_fd(controller->tls_fd);
fds[2].events = POLLIN | POLLPRI;
while (1)
{
/* Lancement d'une phase de surveillance */
ret = poll(fds, 3, -1);
if (ret == -1)
{
if (errno == EINTR) continue;
LOG_ERROR_N("poll");
break;
}
/* Demande expresse d'arrêt des procédures */
if (fds[0].revents)
break;
/* Demande d'actualisation ?! */
assert(fds[1].revents == 0);
/* Le canal est fermé, une sortie doit être demandée... */
if (fds[2].revents & POLLNVAL)
goto closed_exchange;
/**
* Même chose, cf. "TCP: When is EPOLLHUP generated?"
* https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327
*/
if (fds[2].revents & (POLLHUP | POLLRDHUP))
goto closed_exchange;
/* Données présentes en entrée */
if (fds[2].revents & (POLLIN | POLLPRI))
{
init_packed_buffer(&in_pbuf);
status = ssl_recv_packed_buffer(&in_pbuf, controller->tls_fd);
if (!status) goto bad_exchange;
next_command:
status = extract_packed_buffer(&in_pbuf, &command, sizeof(uint32_t), true);
if (!status) goto bad_exchange;
switch (command)
{
case DBC_LIST_BINARIES:
init_packed_buffer(&out_pbuf);
status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_EXISTING_BINARIES },
sizeof(uint32_t), true);
if (!status) goto reply_error;
status = g_cdb_controller_send_existing_binaries(controller, &out_pbuf);
if (!status) goto reply_error;
status = ssl_send_packed_buffer(&out_pbuf, controller->tls_fd);
if (!status) goto reply_error;
exit_packed_buffer(&out_pbuf);
break;
default:
asprintf(&msg, _("Bad protocol command: 0x%08x"), command);
LOG_ERROR(LMT_ERROR, msg);
free(msg);
goto bad_exchange;
break;
}
if (has_more_data_in_packed_buffer(&in_pbuf))
goto next_command;
exit_packed_buffer(&in_pbuf);
continue;
reply_error:
exit_packed_buffer(&out_pbuf);
bad_exchange:
LOG_ERROR(LMT_ERROR, _("Bad exchange"));
assert(0);
exit_packed_buffer(&in_pbuf);
closed_exchange:
break;
}
}
/* On disparaît des écrans... */
g_server_backend_stop(G_SERVER_BACKEND(controller));
return NULL;
}
/******************************************************************************
* *
* Paramètres : controller = support pour le suivi d'une connexion. *
* fd = canal de communication réseau ouvert. *
* peer_name = désignation de la connexion. *
* user = désignation de l'utilisateur de la connexion. *
* *
* Description : Prend en compte une connexion nouvelle d'un utilisateur. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_cdb_controller_add_client(GCdbController *controller, SSL *fd, const char *peer_name, const char *user)
{
controller->tls_fd = fd;
controller->peer_name = strdup(peer_name);
controller->user = strdup(user);
}
/******************************************************************************
* *
* Paramètres : controller = administration d'archives d'analyse. *
* pbuf = paquet à consituer pour un envoi unique. [OUT] *
* *
* Description : Envoie au client la liste des binaires présents. *
* *
* Retour : Bilan de constitution de la réponse. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_cdb_controller_send_existing_binaries(GCdbController *controller, packed_buffer_t *pbuf)
{
bool result; /* Bilan à retourner */
DIR *directory; /* Répertoire de travail */
uleb128_t count; /* Nombre d'éléments détectés */
packed_buffer_t items; /* Liste des éléments trouvés */
struct dirent *item; /* Propriétés d'un élément */
rle_string name; /* Nom à exporter */
bool status; /* Bilan d'une inscription */
int ret; /* Bilan de fermture */
result = false;
directory = opendir(controller->basedir);
if (directory == NULL)
{
LOG_ERROR_N("opendir");
if (errno == ENOENT)
{
count = 0;
result = pack_uleb128(&count, pbuf);
}
goto bad_dir;
}
count = 0;
init_packed_buffer(&items);
for (item = readdir(directory); item != NULL; item = readdir(directory))
{
if (item->d_type != DT_DIR)
continue;
if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0)
continue;
init_static_rle_string(&name, item->d_name);
status = pack_rle_string(&name, &items);
exit_rle_string(&name);
if (!status)
goto reg_error;
count++;
}
result = pack_uleb128(&count, pbuf);
if (result)
result = include_packed_buffer(pbuf, &items);
reg_error:
exit_packed_buffer(&items);
ret = closedir(directory);
if (ret == -1) LOG_ERROR_N("closedir");
bad_dir:
return result;
}