diff options
Diffstat (limited to 'src/analysis/db/cdb.c')
-rw-r--r-- | src/analysis/db/cdb.c | 822 |
1 files changed, 646 insertions, 176 deletions
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c index 9e24f84..6d4b84d 100644 --- a/src/analysis/db/cdb.c +++ b/src/analysis/db/cdb.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * cdb.h - prototypes pour la manipulation des archives au format CDB + * cdb.c - manipulation des archives au format CDB * * Copyright (C) 2014-2019 Cyrille Bagard * @@ -26,10 +26,10 @@ #include <assert.h> #include <errno.h> +#include <fcntl.h> #include <malloc.h> #include <poll.h> #include <pthread.h> -#include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -38,16 +38,20 @@ #include <sys/stat.h> -#include <i18n.h> #include <config.h> +#include <i18n.h> +#include "backend-int.h" #include "collection.h" #include "protocol.h" #include "snapshot.h" +#include "../content.h" +#include "../storage/storage.h" #include "../../common/compression.h" #include "../../common/cpp.h" #include "../../common/extstr.h" +#include "../../common/io.h" #include "../../common/pathname.h" #include "../../common/xml.h" #include "../../core/collections.h" @@ -55,51 +59,70 @@ -/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */ +/* -------------------------- LIEN VERS UN CLIENT CONNECTE -------------------------- */ /* Informations relatives à un client */ typedef struct _cdb_client { - SSL *ssl_fd; /* Canal de communication */ + SSL *tls_fd; /* Canal de communication */ + char *peer_name; /* Désignation du correspondant*/ char *user; /* Utilisateur à l'autre bout */ - uint64_t last_time; /* Date de dernier envoi */ + gint ref_count; /* Décompte d'utilisation */ } cdb_client; +/* Met en place le suivi d'une connexion de client. */ +static cdb_client *create_cdb_client(SSL *, const char *, const char *); + +/* Supprime le suivi d'une connexion de client. */ +static void delete_cdb_client(cdb_client *); + +/* Augmente le décompte d'utilisation d'un suivi de connexion. */ +static void ref_cdb_client(cdb_client *); + +/* Diminue le décompte d'utilisation d'un suivi de connexion. */ +static void unref_cdb_client(cdb_client *); + + + +/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */ + + /* Description d'une archive d'éléments utilisateur (instance) */ struct _GCdbArchive { - GObject parent; /* A laisser en premier */ + GServerBackend parent; /* A laisser en premier */ rle_string hash; /* Empreinte cryptographique */ + rle_string class; /* Nature du contenu analysé */ char *filename; /* Chemin d'accès à l'archive */ char *tmpdir; /* Répertoire de travail */ char *xml_desc; /* Fichier de description */ + char *cnt_file; /* Fichier de contenu binaire */ + + GMutex loading_access; /* Verrou pour l'accès */ + + GList *collections; /* Ensemble de modifications */ GDbSnapshot *snapshot; /* Instantanés de bases SQL */ sqlite3 *db; /* Base de données à manipuler */ - cdb_client *clients; /* Connexions en place */ + cdb_client **clients; /* Connexions en place */ size_t count; /* Quantité de clients */ GMutex clients_access; /* Verrou pour l'accès */ - GThread *process; /* Procédure de traitement */ - GMutex id_access; /* Accès à l'identifiant */ - GCond id_cond; /* Condition d'attente */ - pthread_t process_id; /* Identifiant de la procédure */ - }; /* Description d'une archive d'éléments utilisateur (classe) */ struct _GCdbArchiveClass { - GObjectClass parent; /* A laisser en premier */ + GServerBackendClass parent; /* A laisser en premier */ }; @@ -147,20 +170,135 @@ static void on_collection_extended(GDbCollection *, GDbItem *, GCdbArchive *); /* Assure le traitement des requêtes de clients. */ static void *g_cdb_archive_process(GCdbArchive *); +/* Prend en compte une connexion nouvelle d'un utilisateur. */ +static void g_cdb_archive_add_client(GCdbArchive *, SSL *, const char *, const char *); + +/* Dissocie un utilisateur de l'archive. */ +static void _g_cdb_archive_remove_client(GCdbArchive *, size_t); + +/* Dissocie un utilisateur de l'archive. */ +static void g_cdb_archive_remove_client(GCdbArchive *, size_t); + /* Envoie un paquet de données constitué à tous les clients. */ -static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *, packed_buffer *); +static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *, packed_buffer_t *); /* Envoie à tous les clients la nouvelle liste d'instantanés. */ -static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer *); +static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer_t *); /* Envoie à tous les clients le nouvel instantané courant. */ -static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer *); +static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer_t *); -/* Dissocie un utilisateur de l'archive. */ -static void _g_cdb_archive_remove_client(GCdbArchive *, size_t); -/* Dissocie un utilisateur de l'archive. */ -static void g_cdb_archive_remove_client(GCdbArchive *, size_t); + +/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */ + + +/* Prépare une courte réponse à envoyer à un client connecté. */ +static bool craft_server_short_answer(DBCommand, uleb128_t, packed_buffer_t *); + +/* Enregistre le contenu binaire lié à une analyse. */ +static bool g_cdb_archive_set_content(GCdbArchive *, packed_buffer_t *, packed_buffer_t *); + + + +/* -------------------------- LIEN VERS UN CLIENT CONNECTE -------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* user = désignation de l'utilisateur de la connexion. * +* * +* Description : Met en place le suivi d'une connexion de client. * +* * +* Retour : Structure dédiée construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static cdb_client *create_cdb_client(SSL *fd, const char *peer_name, const char *user) +{ + cdb_client *result; /* Fiche d'entité à retourner */ + + result = malloc(sizeof(cdb_client)); + + result->tls_fd = fd; + + result->peer_name = strdup(peer_name); + result->user = strdup(user); + + g_atomic_int_set(&result->ref_count, 1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Supprime le suivi d'une connexion de client. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void delete_cdb_client(cdb_client *client) +{ + assert(g_atomic_int_get(&client->ref_count) == 0); + + SSL_free(client->tls_fd); + + free(client->peer_name); + free(client->user); + + free(client); + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Augmente le décompte d'utilisation d'un suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ref_cdb_client(cdb_client *client) +{ + g_atomic_int_inc(&client->ref_count); + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Diminue le décompte d'utilisation d'un suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unref_cdb_client(cdb_client *client) +{ + if (g_atomic_int_dec_and_test(&client->ref_count)) + delete_cdb_client(client); + +} @@ -170,7 +308,7 @@ static void g_cdb_archive_remove_client(GCdbArchive *, size_t); /* Indique le type défini pour une une archive d'éléments utilisateur. */ -G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_OBJECT); +G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_SERVER_BACKEND); /****************************************************************************** @@ -188,12 +326,20 @@ G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_OBJECT); static void g_cdb_archive_class_init(GCdbArchiveClass *klass) { GObjectClass *object; /* Autre version de la classe */ + GServerBackendClass *backend; /* Classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_cdb_archive_dispose; object->finalize = (GObjectFinalizeFunc)g_cdb_archive_finalize; + backend = G_SERVER_BACKEND_CLASS(klass); + + backend->thread_name = "cdb_archiver"; + backend->thread_func = (GThreadFunc)g_cdb_archive_process; + + backend->add_client = (add_backend_client_fc)g_cdb_archive_add_client; + } @@ -212,18 +358,22 @@ static void g_cdb_archive_class_init(GCdbArchiveClass *klass) static void g_cdb_archive_init(GCdbArchive *archive) { setup_empty_rle_string(&archive->hash); + setup_empty_rle_string(&archive->class); + archive->filename = NULL; archive->tmpdir = NULL; + archive->xml_desc = NULL; + + archive->cnt_file = NULL; + g_mutex_init(&archive->loading_access); archive->collections = create_collections_list(); archive->snapshot = NULL; + archive->db = NULL; g_mutex_init(&archive->clients_access); - g_mutex_init(&archive->id_access); - g_cond_init(&archive->id_cond); - } @@ -241,13 +391,14 @@ static void g_cdb_archive_init(GCdbArchive *archive) static void g_cdb_archive_dispose(GCdbArchive *archive) { - g_clear_object(&archive->snapshot); - - g_cond_clear(&archive->id_cond); - g_mutex_clear(&archive->id_access); + g_server_backend_stop(G_SERVER_BACKEND(archive)); g_mutex_clear(&archive->clients_access); + g_clear_object(&archive->snapshot); + + g_mutex_clear(&archive->loading_access); + G_OBJECT_CLASS(g_cdb_archive_parent_class)->dispose(G_OBJECT(archive)); } @@ -281,14 +432,19 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) #endif } + if (archive->cnt_file != NULL) + free(archive->cnt_file); + if (archive->xml_desc != NULL) free(archive->xml_desc); - free(archive->filename); - if (archive->tmpdir != NULL) free(archive->tmpdir); + if (archive->filename != NULL) + free(archive->filename); + + exit_rle_string(&archive->class); exit_rle_string(&archive->hash); G_OBJECT_CLASS(g_cdb_archive_parent_class)->finalize(G_OBJECT(archive)); @@ -301,6 +457,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) * Paramètres : basedir = répertoire de stockage des enregistrements. * * tmpdir = répertoire de travail temporaire. * * hash = empreinte du binaire à représenter. * +* class = nature du contenu analysé associé. * * error = indication éventuelle en cas d'échec. [OUT] * * * * Description : Définit ou ouvre une archive d'éléments utilisateur. * @@ -312,7 +469,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) * * ******************************************************************************/ -GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, DBError *error) +GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, const rle_string *class, DBError *error) { GCdbArchive *result; /* Adresse à retourner */ int ret; /* Retour d'un appel */ @@ -321,13 +478,16 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl result = g_object_new(G_TYPE_CDB_ARCHIVE, NULL); dup_into_rle_string(&result->hash, get_rle_string(hash)); + dup_into_rle_string(&result->class, get_rle_string(class)); *error = DBE_SYS_ERROR; /* Chemin de l'archive */ result->filename = strdup(basedir); - result->filename = stradd(result->filename, hash->data); + result->filename = stradd(result->filename, get_rle_string(hash)); + result->filename = stradd(result->filename, "-"); + result->filename = stradd(result->filename, get_rle_string(class)); result->filename = stradd(result->filename, ".cdb.tar.xz"); if (!mkpath(result->filename)) @@ -340,11 +500,8 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl if (!mkpath(tmpdir)) goto error; - ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, get_rle_string(hash)); - if (ret == -1) goto no_tmp; - - ret = ensure_path_exists(result->xml_desc); - if (ret == -1) goto no_tmp; + result->xml_desc = g_cdb_archive_get_tmp_filename(result, "desc.xml"); + if (result->xml_desc == NULL) goto no_tmp; /* Création de l'archive si elle n'existe pas */ @@ -355,7 +512,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl /* Le soucis ne vient pas de l'absence du fichier... */ if (errno != ENOENT) goto error; - result->snapshot = g_db_snapshot_new_empty(tmpdir, get_rle_string(hash), result->collections); + result->snapshot = g_db_snapshot_new_empty(result, result->collections); if (result->snapshot == NULL) goto error; @@ -426,6 +583,47 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl /****************************************************************************** * * * Paramètres : archive = informations quant à l'archive à interpréter. * +* suffix = fin du nom de fichier à définir. * +* * +* Description : Construit un chemin pour un fichier propre à l'archive. * +* * +* Retour : Chemin de fichier à utiliser ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_cdb_archive_get_tmp_filename(const GCdbArchive *archive, const char *suffix) +{ + char *result; /* Chemin à retourner */ + int ret; /* Retour d'un appel */ + + ret = asprintf(&result, "%s" G_DIR_SEPARATOR_S "%s_%s", + archive->tmpdir, get_rle_string(&archive->hash), suffix); + + if (ret == -1) + result = NULL; + + else + { + ret = ensure_path_exists(result); + + if (ret == -1) + { + free(result); + result = NULL; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : archive = informations quant à l'archive à interpréter. * * * * Description : Ouvre une archive avec tous les éléments à conserver. * * * @@ -475,8 +673,7 @@ static DBError g_cdb_archive_read(GCdbArchive *archive) goto load_error; } - archive->snapshot = g_db_snapshot_new_from_xml(archive->tmpdir, get_rle_string(&archive->hash), - xdoc, context); + archive->snapshot = g_db_snapshot_new_from_xml(archive, xdoc, context); close_xml_file(xdoc, context); @@ -503,7 +700,7 @@ static DBError g_cdb_archive_read(GCdbArchive *archive) ret = archive_read_open_filename(in, archive->filename, 10240 /* ?! */); if (ret != ARCHIVE_OK) goto bad_archive; - status = g_db_snapshot_fill(archive->snapshot, in); + status = g_db_snapshot_fill(archive->snapshot, in, archive); if (!status) goto load_error; result = DBE_NONE; @@ -616,18 +813,26 @@ DBError g_cdb_archive_write(const GCdbArchive *archive) * * * Paramètres : archive = informations quant à l'archive à consulter. * * hash = empreinte extérieure à comparer. * +* class = nature du contenu analysé. * * * -* Description : Détermine si une empreinte correspond à celle d'une archive. * +* Description : Détermine l'archive correspond à une cible recherchée. * * * -* Retour : Résultat de la comparaison : -1, 0 ou 1. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *hash) +bool g_cdb_archive_compare_is_suitable_for(const GCdbArchive *archive, const rle_string *hash, const rle_string *class) { - return cmp_rle_string(&archive->hash, hash); + bool result; /* Bilan à retourner */ + + result = (cmp_rle_string(&archive->hash, hash) == 0); + + if (result) + result = (cmp_rle_string(&archive->class, class) == 0); + + return result; } @@ -792,7 +997,7 @@ static void g_cdb_archive_register_signals(GCdbArchive *archive) static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArchive *archive) { - packed_buffer pbuf; /* Tampon d'émission */ + packed_buffer_t pbuf; /* Tampon d'émission */ size_t i; /* Boucle de parcours */ bool status; /* Bilan d'un envoi de retour */ @@ -804,7 +1009,7 @@ static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArc for (i = 0; i < archive->count && status; i++) { - status = ssl_send_packed_buffer(&pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&pbuf, archive->clients[i]->tls_fd); if (!status) LOG_ERROR(LMT_ERROR, _("Failed to send some DB update")); @@ -832,66 +1037,91 @@ static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArc static void *g_cdb_archive_process(GCdbArchive *archive) { + GServerBackend *base; /* Base de l'instance */ + cdb_client **clients; /* Clients surveillés */ + size_t last_count; /* Quantité de ces clients */ struct pollfd *fds; /* Surveillance des flux */ nfds_t nfds; /* Quantité de ces flux */ nfds_t i; /* Boucle de parcours */ int ret; /* Bilan d'un appel */ - packed_buffer in_pbuf; /* Tampon de réception */ + packed_buffer_t 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 */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ GDbCollection *collec; /* Collection visée au final */ bool reload; /* Besoin de rechargement */ char *msg; /* Erreur à faire remonter */ - void interrupt_poll_with_sigusr1(int sig) { }; + base = G_SERVER_BACKEND(archive); - signal(SIGUSR1, interrupt_poll_with_sigusr1); - - g_mutex_lock(&archive->id_access); - archive->process_id = pthread_self(); - g_cond_signal(&archive->id_cond); - g_mutex_unlock(&archive->id_access); + clients = NULL; + last_count = 0; fds = NULL; while (1) { + /* Réinitialisation ? */ + + for (i = 0; i < last_count; i++) + unref_cdb_client(clients[i]); + /* Reconstitution d'une liste à jour */ g_mutex_lock(&archive->clients_access); - nfds = archive->count; + last_count = archive->count; + + clients = realloc(clients, last_count * sizeof(cdb_client)); + + for (i = 0; i < last_count; i++) + { + clients[i] = archive->clients[i]; + ref_cdb_client(clients[i]); + } + + nfds = last_count + 2; fds = realloc(fds, nfds * sizeof(struct pollfd)); - for (i = 0; i < nfds; i++) + for (i = 0; i < (nfds - 2); i++) { - fds[i].fd = SSL_get_fd(archive->clients[i].ssl_fd); + fds[i].fd = SSL_get_fd(clients[i]->tls_fd); fds[i].events = POLLIN | POLLPRI; } g_mutex_unlock(&archive->clients_access); - if (nfds == 0) + if (nfds == 2) goto gcap_no_more_clients; + fds[nfds - 2].fd = base->stop_ctrl[0]; + fds[nfds - 2].events = POLLIN | POLLPRI; + + fds[nfds - 1].fd = base->refresh_ctrl[0]; + fds[nfds - 1].events = POLLIN | POLLPRI; + /* Lancement d'une phase de surveillance */ ret = poll(fds, nfds, -1); if (ret == -1) { - if (errno == EINTR) continue; - LOG_ERROR_N("poll"); break; - } + /* Demande expresse d'arrêt des procédures */ + if (fds[nfds - 2].revents) + break; + + /* Demande d'actualisation */ + if (fds[nfds - 1].revents) + continue; + /* Traitement des requêtes reçues */ - for (i = 0; i < nfds; i++) + for (i = 0; i < (nfds - 1); i++) { /* Le canal est fermé, une sortie doit être demandée... */ if (fds[i].revents & POLLNVAL) @@ -910,7 +1140,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) { init_packed_buffer(&in_pbuf); - status = ssl_recv_packed_buffer(&in_pbuf, archive->clients[i].ssl_fd); + status = ssl_recv_packed_buffer(&in_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_exchange; next_command: @@ -920,13 +1150,21 @@ static void *g_cdb_archive_process(GCdbArchive *archive) command = tmp32; + init_packed_buffer(&out_pbuf); + switch (command) { + case DBC_SET_CONTENT: + status = g_cdb_archive_set_content(archive, &in_pbuf, &out_pbuf); + break; + + + case DBC_SAVE: error = g_cdb_archive_write(archive); - init_packed_buffer(&out_pbuf); + //init_packed_buffer(&out_pbuf); status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, sizeof(uint32_t), true); @@ -935,7 +1173,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) status = extend_packed_buffer(&out_pbuf, (uint32_t []) { error }, sizeof(uint32_t), true); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -976,10 +1214,10 @@ static void *g_cdb_archive_process(GCdbArchive *archive) status = extend_packed_buffer(&out_pbuf, (uint8_t []) { 0x0 }, sizeof(uint8_t), true); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; - exit_packed_buffer(&out_pbuf); + //exit_packed_buffer(&out_pbuf); break; @@ -998,7 +1236,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) &in_pbuf, &out_pbuf, archive->db); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -1010,10 +1248,12 @@ static void *g_cdb_archive_process(GCdbArchive *archive) if (!g_cdb_archive_send_snapshot_update(archive, &out_pbuf)) goto critical_error; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + status = true; - exit_packed_buffer(&out_pbuf); + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; + + //exit_packed_buffer(&out_pbuf); break; @@ -1022,10 +1262,10 @@ static void *g_cdb_archive_process(GCdbArchive *archive) if (!g_cdb_archive_send_snapshot_change(archive, &out_pbuf)) goto critical_error; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; - exit_packed_buffer(&out_pbuf); + //exit_packed_buffer(&out_pbuf); break; @@ -1100,7 +1340,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) case DBC_CREATE_SNAPSHOT: - error = g_db_snapshot_create(archive->snapshot, archive->db); + error = g_db_snapshot_create(archive->snapshot, archive->db, archive); if (error == DBE_NONE) { @@ -1144,6 +1384,17 @@ static void *g_cdb_archive_process(GCdbArchive *archive) } + if (!status) + goto gcap_bad_reply; + + if (get_packed_buffer_payload_length(&out_pbuf) > 0) + { + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + if (!status) goto gcap_bad_reply; + } + + exit_packed_buffer(&out_pbuf); + if (has_more_data_in_packed_buffer(&in_pbuf)) goto next_command; @@ -1185,12 +1436,13 @@ static void *g_cdb_archive_process(GCdbArchive *archive) gcap_no_more_clients: - archive->process = NULL; + g_server_backend_stop(G_SERVER_BACKEND(archive)); + + for (i = 0; i < last_count; i++) + unref_cdb_client(clients[i]); - g_mutex_lock(&archive->id_access); - archive->process_id = 0; - g_cond_signal(&archive->id_cond); - g_mutex_unlock(&archive->id_access); + if (clients != NULL) + free(clients); if (fds != NULL) free(fds); @@ -1202,6 +1454,170 @@ static void *g_cdb_archive_process(GCdbArchive *archive) /****************************************************************************** * * +* Paramètres : archive = 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 LoadingStatusHint g_cdb_archive_compute_loading_hint(GCdbArchive *archive) +{ + LoadingStatusHint result; /* Statut à retourner */ + + + // Try + // g_mutex_lock(&archive->loading_access); + + + + // cnt_file + + if (archive->cnt_file == NULL) + result = LSH_NEED_CONTENT; + + else + result = LSH_NEED_FORMAT; + + + + + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : archive = 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_archive_add_client(GCdbArchive *archive, SSL *fd, const char *peer_name, const char *user) +{ + cdb_client *client; /* Nouvelle fiche d'entité */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + LoadingStatusHint hint; /* Statut de chargement */ + bool status; /* Bilan de lecture initiale */ + + client = create_cdb_client(fd, peer_name, user); + + /** + * Le verrou encadrant les évolutions des contenus initiaux doit englober + * l'extension de la liste des clients. + * + * En effet, une évolution partielle peut intervenir dans la fonction + * g_cdb_archive_process(), à un moment au seul le verrou dans les + * évolutions sera posé (g_cdb_archive_set_content() par exemple). + * + * Or g_cdb_archive_compute_loading_hint() doit fournir ici un état qui ne + * varie pas entre le calcul et l'envoi. Donc verrous sur les clients et + * l'état de l'archive doivent englover l'ensemble des traitements ci-après. + */ + + g_mutex_lock(&archive->loading_access); + + g_mutex_lock(&archive->clients_access); + + hint = g_cdb_archive_compute_loading_hint(archive); + + if (hint != LSH_READY) + hint = (archive->count == 0 ? hint : LSH_ON_WAIT_LIST); + + init_packed_buffer(&out_pbuf); + + status = craft_server_short_answer(DBC_LOADING_STATUS, hint, &out_pbuf); + + if (status) + status = ssl_send_packed_buffer(&out_pbuf, fd); + + exit_packed_buffer(&out_pbuf); + + if (status) + { + archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client *)); + + archive->clients[archive->count - 1] = client; + + } + + g_mutex_unlock(&archive->clients_access); + + g_mutex_unlock(&archive->loading_access); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* index = indice de l'utilisateur concerné. * +* * +* Description : Dissocie un utilisateur de l'archive. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void _g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) +{ + assert(!g_mutex_trylock(&archive->clients_access)); + + unref_cdb_client(archive->clients[index]); + + if ((index + 1) < archive->count) + memmove(&archive->clients[index], &archive->clients[index + 1], + (archive->count - index - 1) * sizeof(cdb_client *)); + + archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client *)); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* index = indice de l'utilisateur concerné. * +* * +* Description : Dissocie un utilisateur de l'archive. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) +{ + g_mutex_lock(&archive->clients_access); + + _g_cdb_archive_remove_client(archive, index); + + g_mutex_unlock(&archive->clients_access); + +} + + +/****************************************************************************** +* * * Paramètres : archive = archive à connecter avec un utilisateur. * * pbuf = paquet de données à émettre. * * * @@ -1213,7 +1629,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) * * ******************************************************************************/ -static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed_buffer *pbuf) +static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed_buffer_t *pbuf) { size_t i; /* Boucle de parcours */ bool status; /* Bilan d'une émission */ @@ -1222,7 +1638,7 @@ static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed for (i = 0; i < archive->count; i++) { - status = ssl_send_packed_buffer(pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(pbuf, archive->clients[i]->tls_fd); if (!status) { log_variadic_message(LMT_ERROR, _("Error while replying to client %zu"), i); @@ -1252,11 +1668,11 @@ static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed * * ******************************************************************************/ -static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buffer *pbuf) +static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ bool do_send; /* Réalisation de l'émission */ - packed_buffer out_pbuf; /* Tampon d'émission */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ do_send = (pbuf == NULL); @@ -1301,11 +1717,11 @@ static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buff * * ******************************************************************************/ -static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buffer *pbuf) +static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ bool do_send; /* Réalisation de l'émission */ - packed_buffer out_pbuf; /* Tampon d'émission */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ snapshot_id_t id; /* Identifiant d'instantané */ do_send = (pbuf == NULL); @@ -1339,132 +1755,186 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buff } + +/* ---------------------------------------------------------------------------------- */ +/* PRISES EN COMPTE DES COMMANDES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* fd = canal de communication réseau ouvert. * +* Paramètres : cmd = commande à l'origine d'un traitement. * +* value = valeur à communiquer. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* * * -* Description : Associe un nouvel utilisateur à l'archive. * +* Description : Prépare une courte réponse à envoyer à un client connecté. * * * -* Retour : - * +* Retour : Indication pour le maintien de la communication. * * * * Remarques : - * * * ******************************************************************************/ -void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd) +static bool craft_server_short_answer(DBCommand cmd, uleb128_t value, packed_buffer_t *out_pbuf) { - X509 *peer_cert; /* Certificat présenté */ - volatile pthread_t *process_id; /* Identifiant de la procédure */ + bool result; /* Bilan à retourner */ - /** - * La situation est un peu compliquée lors de l'accueil d'un nouveau client : - * - * - soit on envoie tous les éléments à prendre en compte, mais on doit - * bloquer la base de données jusqu'à l'intégration pleine et entière - * du client, afin que ce dernier ne loupe pas l'envoi d'un nouvel - * élément entre temps. - * - * - soit on intègre le client et celui ci demande des mises à jour - * collection par collection ; c'est également à lui qui revient le rejet - * des éléments envoyés en solitaires avant la réception de la base - * complète. - * - * On fait le choix du second scenario ici, du fait de la difficulté - * de maîtriser facilement la reconstitution d'une liste de clients dans - * g_cdb_archive_process() depuis un autre flot d'exécution. - */ + init_packed_buffer(out_pbuf); - /* Ajout dans la liste officielle */ + result = extend_packed_buffer(out_pbuf, (uint32_t []) { cmd }, sizeof(uint32_t), true); - g_mutex_lock(&archive->clients_access); + if (result) + result = pack_uleb128((uleb128_t []){ value }, out_pbuf); - archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client)); + return result; - archive->clients[archive->count - 1].ssl_fd = fd; +} - peer_cert = SSL_get_peer_certificate(fd); - archive->clients[archive->count - 1].user = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, -1); +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* in_pbuf = paquet à consulter. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* +* * +* Description : Enregistre le contenu binaire lié à une analyse. * +* * +* Retour : Indication pour le maintien de la communication. * +* * +* Remarques : - * +* * +******************************************************************************/ - X509_free(peer_cert); +static bool g_cdb_archive_set_content(GCdbArchive *archive, packed_buffer_t *in_pbuf, packed_buffer_t *out_pbuf) +{ + bool result; /* Bilan à retourner */ + DBError error; /* Bilan d'une opération */ + uleb128_t data_length; /* Taille du contenu stocké */ + void *data; /* Données du stockage */ + packed_buffer_t test_pbuf; /* Copie des données pour test */ + uleb128_t pos; /* Position du contenu */ + GObjectStorage *storage; /* Gestionnaire de stockage */ + GSerializableObject *content; /* Contenu restitué */ + const gchar *hash; /* Empreinte de ce contenu */ + int fd; /* Flux ouvert en écriture */ + bool status; /* Bilan d'une écriture */ + LoadingStatusHint hint; /* Statut de chargement */ - /* Démarrage ou redémarrage du processus d'écoute */ + result = true; + error = DBE_NONE; - if (archive->process == NULL) - { - archive->process = g_thread_new("cdb_process", (GThreadFunc)g_cdb_archive_process, archive); + /* Récupération de la charge utile */ + + result = unpack_uleb128(&data_length, in_pbuf); + if (!result) goto exit; + + data = malloc(data_length); + + result = extract_packed_buffer(in_pbuf, data, data_length, false); + if (!result) goto free_and_exit; + + /* Validation de l'empreinte du contenu */ + + init_packed_buffer(&test_pbuf); - /* On attend que le processus parallèle soit prêt */ + result = extend_packed_buffer(&test_pbuf, data, data_length, false); + if (!result) goto check_failure; - process_id = &archive->process_id; + rewind_packed_buffer(&test_pbuf); - g_mutex_lock(&archive->id_access); - while (process_id == 0) - g_cond_wait(&archive->id_cond, &archive->id_access); - g_mutex_unlock(&archive->id_access); + result = unpack_uleb128(&pos, &test_pbuf); + if (!result) goto check_failure; + storage = g_object_storage_load(&test_pbuf); + if (storage == NULL) + { + result = false; + goto check_failure; } - else - pthread_kill(archive->process_id, SIGUSR1); - g_mutex_unlock(&archive->clients_access); + content = g_object_storage_load_object(storage, "contents", pos); + if (!G_IS_BIN_CONTENT(content)) + { + result = false; + goto storage_check_failure; + } -} + hash = g_binary_content_get_checksum(G_BIN_CONTENT(content)); + if (strcmp(hash, get_rle_string(&archive->hash)) != 0) + error = DBE_WRONG_HASH; -/****************************************************************************** -* * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* index = indice de l'utilisateur concerné. * -* * -* Description : Dissocie un utilisateur de l'archive. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + g_object_unref(G_OBJECT(content)); -static void _g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) -{ - cdb_client *client; /* Client à traiter */ + storage_check_failure: - assert(!g_mutex_trylock(&archive->clients_access)); + g_object_unref(G_OBJECT(storage)); - client = &archive->clients[index]; + check_failure: - SSL_free(client->ssl_fd); - free(client->user); + exit_packed_buffer(&test_pbuf); - if ((index + 1) < archive->count) - memmove(&archive->clients[index], &archive->clients[index + 1], - (archive->count - index - 1) * sizeof(cdb_client)); + if (!result) goto free_and_exit; - archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client)); + /* Enregistrement sur disque */ -} + if (error == DBE_NONE) + { + if (archive->cnt_file != NULL) + free(archive->cnt_file); + archive->cnt_file = g_cdb_archive_get_tmp_filename(archive, "storedcontent.bin"); + if (archive->cnt_file == NULL) + { + error = DBE_SYS_ERROR; + goto save_error; + } -/****************************************************************************** -* * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* index = indice de l'utilisateur concerné. * -* * -* Description : Dissocie un utilisateur de l'archive. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + fd = open(archive->cnt_file, O_WRONLY | O_CREAT, 0600); + if (fd == -1) + { + error = DBE_SYS_ERROR; + goto save_error; + } -static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) -{ - g_mutex_lock(&archive->clients_access); + status = safe_write(fd, data, data_length); - _g_cdb_archive_remove_client(archive, index); + if (!status) + { + unlink(archive->cnt_file); + free(archive->cnt_file); + archive->cnt_file = NULL; - g_mutex_unlock(&archive->clients_access); + error = DBE_SYS_ERROR; + + } + + close(fd); + + save_error: + + ; + + } + + /* Formulation de la réponse */ + + result = craft_server_short_answer(DBC_SET_CONTENT, error, out_pbuf); + + if (result && error == DBE_NONE) + { + hint = g_cdb_archive_compute_loading_hint(archive); + + result = craft_server_short_answer(DBC_LOADING_STATUS, hint, out_pbuf); + + } + + free_and_exit: + + free(data); + + exit: + + return result; } |