diff options
Diffstat (limited to 'src/analysis/db/server.c')
-rw-r--r-- | src/analysis/db/server.c | 424 |
1 files changed, 335 insertions, 89 deletions
diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c index 258a66c..ad7929f 100644 --- a/src/analysis/db/server.c +++ b/src/analysis/db/server.c @@ -42,7 +42,9 @@ #include "auth.h" +#include "backend.h" #include "cdb.h" +#include "controller.h" #include "protocol.h" #include "misc/rlestr.h" #include "../../common/extstr.h" @@ -92,8 +94,10 @@ struct _GHubServer GThread *listener; /* Procédure de traitement */ - GList *archives; /* Liste des binaires ouverts */ - GMutex mutex; /* Verrou pour l'accès */ + GList *controllers; /* Liste des administrateurs */ + GMutex ctrl_mutex; /* Verrou pour l'accès */ + GList *archives; /* Liste des aanlystes */ + GMutex ar_mutex; /* Verrou pour l'accès */ GMutex wait_mutex; /* Accès à la condition */ GCond wait_cond; /* Attente de signal */ @@ -136,6 +140,18 @@ static int g_hub_server_verify(int, X509_STORE_CTX *); /* Assure l'accueil des nouveaux clients. */ static void *g_hub_server_listener(GHubServer *); +/* Assure l'accueil des nouveaux clients administrateurs. */ +static GServerBackend *g_hub_server_handle_admin(GHubServer *, packed_buffer *, const char *, DBError *, bool *); + +/* Assure l'accueil des nouveaux clients analystes. */ +static GServerBackend *g_hub_server_handle_analyst(GHubServer *, packed_buffer *, const char *, DBError *, bool *); + +/* Enregistre dans une liste interne un support de suivi. */ +static void g_hub_server_register_backend(GHubServer *, GServerBackend *); + +/* Suit les variations du compteur de références d'un greffon. */ +static void on_backend_ref_toggle(GHubServer *, GServerBackend *, gboolean); + /* Indique le type défini pour une description de serveur à l'écoute. */ @@ -190,7 +206,10 @@ static void g_hub_server_init(GHubServer *server) server->unlock_socket = NULL; server->lock_fd = -1; - g_mutex_init(&server->mutex); + server->controllers = NULL; + g_mutex_init(&server->ctrl_mutex); + server->archives = NULL; + g_mutex_init(&server->ar_mutex); g_mutex_init(&server->wait_mutex); g_cond_init(&server->wait_cond); @@ -216,15 +235,23 @@ static void g_hub_server_dispose(GHubServer *server) g_hub_server_stop(server); + for (iter = g_list_first(server->controllers); + iter != NULL; + iter = g_list_first(server->controllers)) + { + g_object_unref(G_OBJECT(iter->data)); + } + + g_mutex_clear(&server->ctrl_mutex); + for (iter = g_list_first(server->archives); iter != NULL; iter = g_list_first(server->archives)) { g_object_unref(G_OBJECT(iter->data)); - server->archives = g_list_delete_link(server->archives, iter); } - g_mutex_clear(&server->mutex); + g_mutex_clear(&server->ar_mutex); g_mutex_clear(&server->wait_mutex); g_cond_clear(&server->wait_cond); @@ -721,18 +748,16 @@ static void *g_hub_server_listener(GHubServer *server) gen_sockaddr_t peer; /* Adresse cliente */ int fd; /* Canal établi vers un client */ SSL *tls_fd; /* Même canal, mais sécurisé */ - rle_string hash; /* Empreinte du binaire visé */ + GServerBackend *backend; /* Support de suivi créé */ const char *ip; /* Statut de la conversion */ char *peer_name; /* Désignation du correspondant*/ DBError error; /* Validation de la connexion */ - GCdbArchive *archive; /* Destinataire final du client*/ - GList *iter; /* Boucle de parcours */ packed_buffer in_pbuf; /* Tampon de réception */ bool status; /* Bilan d'une opération */ uint32_t cmd; /* Commande initiale lue */ uint32_t version; /* Version du client lue */ - char *basedir; /* Répertoire de stockage */ - char *tmpdir; /* Répertoire de travail */ + uint32_t role; /* Rôle visé par le client */ + bool new; /* Besoin d'ajout à une liste */ packed_buffer out_pbuf; /* Tampon d'émission */ fds.fd = server->fd; @@ -782,12 +807,10 @@ static void *g_hub_server_listener(GHubServer *server) goto invalid_conn; } - /* Initialisation à vide pour les sorties en erreur */ - - setup_empty_rle_string(&hash); - /* Construction d'une représentation */ + backend = NULL; + if (*((sa_family_t *)&peer) == AF_UNIX) peer_name = strdup(server->desc); @@ -799,7 +822,7 @@ static void *g_hub_server_listener(GHubServer *server) if (ip == NULL) { LOG_ERROR_N("inet_ntop"); - goto id_error; + goto ip_error; } snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet4_addr.sin_port)); @@ -814,7 +837,7 @@ static void *g_hub_server_listener(GHubServer *server) if (ip == NULL) { LOG_ERROR_N("inet_ntop"); - goto id_error; + goto ip_error; } snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet6_addr.sin6_port)); @@ -825,14 +848,12 @@ static void *g_hub_server_listener(GHubServer *server) goto invalid_conn; error = DBE_NONE; - archive = NULL; - - iter = NULL; /** * Le premier "paquet" reçu de la part d'un client doit contenir les informations suivantes : - * - la commande 'DBC_HELO'. - * - le numéro de version du client. + * - la commande 'DBC_HELO' ; + * - le numéro de version du client ; + * - le rôle attendu. * - l'empreinte du binaire analysé. * * Tout ceci est à synchroniser avec la fonction g_db_client_start(). @@ -846,7 +867,7 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the initial packet from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } status = extract_packed_buffer(&in_pbuf, &cmd, sizeof(uint32_t), true); @@ -855,7 +876,7 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the initial command from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } status = extract_packed_buffer(&in_pbuf, &version, sizeof(uint32_t), true); @@ -864,16 +885,16 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the protocol version from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } - status = unpack_rle_string(&hash, &in_pbuf); + status = extract_packed_buffer(&in_pbuf, &role, sizeof(uint32_t), true); if (!status) { - log_variadic_message(LMT_ERROR, _("Error while getting the binary hash from '%s'..."), + log_variadic_message(LMT_ERROR, _("Error while getting the expected role from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } if (cmd != DBC_HELO) @@ -881,65 +902,48 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("The client from '%s' did not introduce itself!"), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } if (version != CDB_PROTOCOL_VERSION) { - log_variadic_message(LMT_ERROR, _("The client from '%s' does not use the same protocol: 0x%08x vs 0x%08x..."), + log_variadic_message(LMT_ERROR, + _("The client from '%s' does not use the same protocol: 0x%08x vs 0x%08x..."), peer_name, be32toh(version), CDB_PROTOCOL_VERSION); error = DBE_WRONG_VERSION; - goto error_sending; + goto error_receiving; } - if (is_rle_string_empty(&hash)) + switch (role) { - log_variadic_message(LMT_ERROR, _("The submitted binary hash from '%s' is empty!"), - peer_name); - error = DBE_BAD_EXCHANGE; - goto error_sending; - } - - /** - * On met en place le maximum ici, de manière à pouvoir indiquer une erreur - * en cas d'échec, et être le plus précis possible dans la courte réponse. - */ - - assert(error == DBE_NONE); - - for (iter = g_list_first(server->archives); - iter != NULL; - iter = g_list_next(iter)) - { - archive = G_CDB_ARCHIVE(iter->data); - if (g_cdb_archive_compare_hash(archive, &hash) == 0) + case CRL_ADMIN: + backend = g_hub_server_handle_admin(server, &in_pbuf, peer_name, &error, &new); break; - } - if (iter == NULL) - { - basedir = strdup(server->working); - basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + case CRL_ANALYST: + backend = g_hub_server_handle_analyst(server, &in_pbuf, peer_name, &error, &new); + break; - tmpdir = strdup(server->working); - tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S); + default: + log_variadic_message(LMT_ERROR, _("Unknown client role requested by '%s'"), + peer_name); + backend = NULL; + error = DBE_BAD_EXCHANGE; + new = false; + break; - archive = g_cdb_archive_new(basedir, tmpdir, &hash, &error); + } - free(tmpdir); - free(basedir); + assert((backend == NULL && error != DBE_NONE) || (backend != NULL && error == DBE_NONE)); - } + error_receiving: /** * Le serveur doit répondre pour un message type : - * - la commande 'DBC_WELCOME'. - * - un identifiant d'erreur ('DBE_NONE', 'DBE_BAD_EXCHANGE' - * ou 'DBE_WRONG_VERSION' ... 'DBE_LOADING_ERROR'). + * - la commande 'DBC_WELCOME' ; + * - un identifiant d'erreur. */ - error_sending: - exit_packed_buffer(&in_pbuf); init_packed_buffer(&out_pbuf); @@ -959,41 +963,28 @@ static void *g_hub_server_listener(GHubServer *server) * lors des échanges initiaux, car ces derniers seraient alors précédés des mises à jour... */ - if (archive != NULL) + if (backend != NULL) { - assert(error == DBE_NONE); - - /* Si l'archive a été créée pour l'occasion... */ - if (iter == NULL) - server->archives = g_list_append(server->archives, archive); - - g_cdb_archive_add_client(archive, tls_fd); + if (new) + g_hub_server_register_backend(server, backend); - exit_packed_buffer(&out_pbuf); - - free(peer_name); - - exit_rle_string(&hash); - - continue; + g_server_backend_add_client(backend, tls_fd, peer_name); } - assert(error != DBE_NONE); - out_error: - exit_packed_buffer(&out_pbuf); + if (backend != NULL) + g_object_unref(G_OBJECT(backend)); - /* Si l'archive a été créée pour l'occasion... */ - if (iter == NULL && archive != NULL) - g_object_unref(G_OBJECT(archive)); + exit_packed_buffer(&out_pbuf); - id_error: + ip_error: free(peer_name); - exit_rle_string(&hash); + if (backend != NULL) + continue; invalid_conn: @@ -1016,6 +1007,261 @@ static void *g_hub_server_listener(GHubServer *server) /****************************************************************************** * * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* in_pbuf = reste des premières données reçues. * +* peer_name = désignation de la connexion entrante. * +* error = code d'erreur issu du traitement. [OUT] * +* new = indique si le résultat doit être ajouté. [OUT] * +* * +* Description : Assure l'accueil des nouveaux clients administrateurs. * +* * +* Retour : Instance de support de suivi mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GServerBackend *g_hub_server_handle_admin(GHubServer *server, packed_buffer *in_pbuf, const char *peer_name, DBError *error, bool *new) +{ + GCdbController *result; /* Support de suivi à retourner*/ + char *basedir; /* Répertoire de stockage */ + + if (has_more_data_in_packed_buffer(in_pbuf)) + { + log_variadic_message(LMT_ERROR, _("The client from '%s' provided to much data!"), peer_name); + + result = NULL; + + *error = DBE_BAD_EXCHANGE; + *new = false; + + } + else + { + basedir = strdup(server->working); + basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + + result = g_cdb_controller_new(basedir, error); + + free(basedir); + + *new = true; + + } + + return G_SERVER_BACKEND(result); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* in_pbuf = reste des premières données reçues. * +* peer_name = désignation de la connexion entrante. * +* error = code d'erreur issu du traitement. [OUT] * +* new = indique si le résultat doit être ajouté. [OUT] * +* * +* Description : Assure l'accueil des nouveaux clients analystes. * +* * +* Retour : Instance de support de suivi mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GServerBackend *g_hub_server_handle_analyst(GHubServer *server, packed_buffer *in_pbuf, const char *peer_name, DBError *error, bool *new) +{ + GCdbArchive *result; /* Support de suivi à retourner*/ + rle_string hash; /* Empreinte du binaire visé */ + bool status; /* Bilan d'une opération */ + GList *iter; /* Boucle de parcours */ + GCdbArchive *archive; /* Destinataire final du client*/ + char *basedir; /* Répertoire de stockage */ + char *tmpdir; /* Répertoire de travail */ + + result = NULL; + + *error = DBE_BAD_EXCHANGE; + *new = false; + + /* Fin de réception des données envoyées */ + + status = unpack_rle_string(&hash, in_pbuf); + if (!status) + { + log_variadic_message(LMT_ERROR, _("Error while getting the binary hash from '%s'..."), peer_name); + goto error_receiving; + } + + if (is_rle_string_empty(&hash)) + { + log_variadic_message(LMT_ERROR, _("The submitted binary hash from '%s' is empty!"), peer_name); + goto wrong_receiving; + } + + if (has_more_data_in_packed_buffer(in_pbuf)) + { + log_variadic_message(LMT_ERROR, _("The client from '%s' provided to much data!"), peer_name); + goto wrong_receiving; + } + + /* Recherche d'un support existant adapté */ + + g_mutex_lock(&server->ar_mutex); + + for (iter = g_list_first(server->archives); iter != NULL; iter = g_list_next(iter)) + { + archive = G_CDB_ARCHIVE(iter->data); + + if (g_cdb_archive_compare_hash(archive, &hash) == 0) + break; + + } + + if (iter != NULL) + { + result = archive; + g_object_ref(G_OBJECT(result)); + } + + g_mutex_unlock(&server->ar_mutex); + + /* Nouvelle création au besoin */ + + if (result == NULL) + { + basedir = strdup(server->working); + basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + + tmpdir = strdup(server->working); + tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S); + + result = g_cdb_archive_new(basedir, tmpdir, &hash, error); + + free(tmpdir); + free(basedir); + + *new = true; + + } + + wrong_receiving: + + exit_rle_string(&hash); + + error_receiving: + + return (result != NULL ? G_SERVER_BACKEND(result) : NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* backend = support de suivi de connexion. * +* * +* Description : Enregistre dans une liste interne un support de suivi. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hub_server_register_backend(GHubServer *server, GServerBackend *backend) +{ + GList **list; /* Liste à parcourir */ + GMutex *mutex; /* Verrou à manipuler */ + + /* Sélection des éléments concernés */ + + if (G_IS_CDB_CONTROLLER(backend)) + { + list = &server->controllers; + mutex = &server->ctrl_mutex; + } + else if (G_IS_CDB_ARCHIVE(backend)) + { + list = &server->archives; + mutex = &server->ar_mutex; + } + else + assert(false); + + /* Retrait de l'élément inutilisé */ + + g_mutex_lock(mutex); + + g_object_ref(G_OBJECT(backend)); + + *list = g_list_append(*list, backend); + + g_object_add_toggle_ref(G_OBJECT(backend), (GToggleNotify)on_backend_ref_toggle, server); + + g_mutex_unlock(mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* backend = support de suivi de connexion. * +* last = indication sur la valeur du compteur de références.* +* * +* Description : Suit les variations du compteur de références d'un greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_backend_ref_toggle(GHubServer *server, GServerBackend *backend, gboolean last) +{ + GList **list; /* Liste à parcourir */ + GMutex *mutex; /* Verrou à manipuler */ + GList *iter; /* Boucle de parcours */ + + if (last) + { + /* Sélection des éléments concernés */ + + if (G_IS_CDB_CONTROLLER(backend)) + { + list = &server->controllers; + mutex = &server->ctrl_mutex; + } + else if (G_IS_CDB_ARCHIVE(backend)) + { + list = &server->archives; + mutex = &server->ar_mutex; + } + else + assert(false); + + /* Retrait de l'élément inutilisé */ + + g_mutex_lock(mutex); + + for (iter = g_list_first(*list); iter != NULL; iter = g_list_first(*list)) + { + *list = g_list_delete_link(*list, iter); + } + + g_object_remove_toggle_ref(G_OBJECT(backend), (GToggleNotify)on_backend_ref_toggle, server); + + g_mutex_unlock(mutex); + + } + +} + + +/****************************************************************************** +* * * Paramètres : server = serveur pour les accès distants à manipuler. * * backlog = nombre de connexions maximal. * * keep = conservation du serveur en avant plan. * |