/* 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 "backend-int.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 *); /* 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 in_pbuf; /* Tampon de réception */ uint32_t tmp32; /* Valeur sur 32 bits */ bool status; /* Bilan de lecture initiale */ uint32_t command; /* Commande de la requête */ DBError error; /* Bilan d'une opération */ packed_buffer 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_ARCHIVES: /* error = g_cdb_controller_write(archive); init_packed_buffer(&out_pbuf); status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, sizeof(uint32_t), true); if (!status) goto reply_error; status = extend_packed_buffer(&out_pbuf, (uint32_t []) { error }, sizeof(uint32_t), true); 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); } #if 0 /****************************************************************************** * * * Paramètres : controller = archive à connecter avec un utilisateur. * * pbuf = paquet à consituer pour un envoi unique. [OUT] * * * * Description : Envoie à tous les clients la nouvelle liste d'instantanés. * * * * Retour : Bilan de constitution de la réponse. * * * * Remarques : - * * * ******************************************************************************/ static bool g_cdb_controller_send_snapshot_update(GCdbController *controller, packed_buffer *pbuf) { bool result; /* Bilan à retourner */ bool do_send; /* Réalisation de l'émission */ packed_buffer out_pbuf; /* Tampon d'émission */ do_send = (pbuf == NULL); if (pbuf == NULL) pbuf = &out_pbuf; init_packed_buffer(pbuf); result = extend_packed_buffer(pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED }, sizeof(uint32_t), true); if (!result) goto bad_reply; result = g_db_snapshot_pack_all(archive->snapshot, pbuf); if (!result) goto bad_reply; result = extend_packed_buffer(pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false); if (!result) goto bad_reply; bad_reply: if (do_send || !result) exit_packed_buffer(pbuf); return result; } #endif