From 73f5d359d6a0cc15ce463aa4b5595bd7112dec45 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 14 Nov 2021 21:58:40 +0100
Subject: Prevent usage of removed clients.

---
 src/analysis/db/cdb.c | 228 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 187 insertions(+), 41 deletions(-)

diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 08410c4..c5d3af7 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -59,7 +59,7 @@
 
 
 
-/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */
+/* -------------------------- LIEN VERS UN CLIENT CONNECTE -------------------------- */
 
 
 /* Informations relatives à un client */
@@ -69,11 +69,28 @@ typedef struct _cdb_client
     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
 {
@@ -92,7 +109,7 @@ struct _GCdbArchive
     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         */
 
@@ -180,6 +197,107 @@ static bool g_cdb_archive_set_content(GCdbArchive *, packed_buffer_t *, packed_b
 
 
 
+/* -------------------------- 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);
+
+}
+
+
+
 /* ---------------------------------------------------------------------------------- */
 /*                           COEUR DE LA GESTION D'ARCHIVES                           */
 /* ---------------------------------------------------------------------------------- */
@@ -246,6 +364,7 @@ static void g_cdb_archive_init(GCdbArchive *archive)
     archive->collections = create_collections_list();
 
     archive->snapshot = NULL;
+    archive->db = NULL;
 
     g_mutex_init(&archive->clients_access);
 
@@ -882,7 +1001,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].tls_fd);
+        status = ssl_send_packed_buffer(&pbuf, archive->clients[i]->tls_fd);
 
         if (!status)
             LOG_ERROR(LMT_ERROR, _("Failed to send some DB update"));
@@ -911,6 +1030,8 @@ 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          */
@@ -927,20 +1048,38 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
     base = G_SERVER_BACKEND(archive);
 
+    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 + 2;
+        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 - 2); i++)
         {
-            fds[i].fd = SSL_get_fd(archive->clients[i].tls_fd);
+            fds[i].fd = SSL_get_fd(clients[i]->tls_fd);
             fds[i].events = POLLIN | POLLPRI;
         }
 
@@ -993,7 +1132,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
             {
                 init_packed_buffer(&in_pbuf);
 
-                status = ssl_recv_packed_buffer(&in_pbuf, archive->clients[i].tls_fd);
+                status = ssl_recv_packed_buffer(&in_pbuf, clients[i]->tls_fd);
                 if (!status) goto gcap_bad_exchange;
 
  next_command:
@@ -1003,17 +1142,12 @@ 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);
-                        if (!status) goto gcap_bad_reply;
-
-                        status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd);
-                        if (!status) goto gcap_bad_reply;
-
-                        exit_packed_buffer(&out_pbuf);
                         break;
 
 
@@ -1022,7 +1156,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                         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);
@@ -1031,7 +1165,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].tls_fd);
+                        status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd);
                         if (!status) goto gcap_bad_reply;
 
                         exit_packed_buffer(&out_pbuf);
@@ -1072,10 +1206,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].tls_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;
 
@@ -1094,7 +1228,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].tls_fd);
+                        status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd);
                         if (!status) goto gcap_bad_reply;
 
                         exit_packed_buffer(&out_pbuf);
@@ -1106,10 +1240,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].tls_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;
 
@@ -1118,10 +1254,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].tls_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;
 
@@ -1240,6 +1376,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;
 
@@ -1283,6 +1430,12 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
     g_server_backend_stop(G_SERVER_BACKEND(archive));
 
+    for (i = 0; i < last_count; i++)
+        unref_cdb_client(clients[i]);
+
+    if (clients != NULL)
+        free(clients);
+
     if (fds != NULL)
         free(fds);
 
@@ -1328,15 +1481,13 @@ static void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const char *
      * g_cdb_archive_process() depuis un autre flot d'exécution.
      */
 
-    g_mutex_lock(&archive->clients_access);
+    client = create_cdb_client(fd, peer_name, user);
 
-    archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client));
+    g_mutex_lock(&archive->clients_access);
 
-    client = &archive->clients[archive->count - 1];
+    archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client *));
 
-    client->tls_fd = fd;
-    client->peer_name = strdup(peer_name);
-    client->user = strdup(user);
+    archive->clients[archive->count - 1] = client;
 
     g_mutex_unlock(&archive->clients_access);
 
@@ -1358,20 +1509,15 @@ static void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const char *
 
 static void _g_cdb_archive_remove_client(GCdbArchive *archive, size_t index)
 {
-    cdb_client *client;                     /* Client à traiter            */
-
     assert(!g_mutex_trylock(&archive->clients_access));
 
-    client = &archive->clients[index];
-
-    SSL_free(client->tls_fd);
-    free(client->user);
+    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->count - index - 1) * sizeof(cdb_client *));
 
-    archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client));
+    archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client *));
 
 }
 
@@ -1422,7 +1568,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].tls_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);
-- 
cgit v0.11.2-87-g4458