feat(osint): execute DNS lookup action
This commit is contained in:
parent
b1b2320469
commit
6c8113dfea
13 changed files with 720 additions and 18 deletions
13
Makefile
13
Makefile
|
|
@ -115,6 +115,7 @@ TEST_INVESTIGATION_GRAPH_LOAD_TASK := tests/test_investigation_graph_load_task
|
||||||
TEST_GRAPH_NODE_POSITION_DAO := tests/test_graph_node_position_dao
|
TEST_GRAPH_NODE_POSITION_DAO := tests/test_graph_node_position_dao
|
||||||
TEST_OSINT_SELECTION_CONTEXT := tests/test_osint_selection_context
|
TEST_OSINT_SELECTION_CONTEXT := tests/test_osint_selection_context
|
||||||
TEST_OSINT_ACTION_CATALOG := tests/test_osint_action_catalog
|
TEST_OSINT_ACTION_CATALOG := tests/test_osint_action_catalog
|
||||||
|
TEST_OSINT_DNS_QUERY := tests/test_osint_dns_query
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
|
@ -545,6 +546,11 @@ $(TEST_OSINT_ACTION_CATALOG): \
|
||||||
src/models/relation_record.c
|
src/models/relation_record.c
|
||||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||||
|
|
||||||
|
$(TEST_OSINT_DNS_QUERY): \
|
||||||
|
tests/test_osint_dns_query.c \
|
||||||
|
src/models/osint_dns_query.c
|
||||||
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||||
|
|
||||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
|
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
|
||||||
tests/test_investigation_graph_load_task.c \
|
tests/test_investigation_graph_load_task.c \
|
||||||
src/core/investigation_graph_load_task.c \
|
src/core/investigation_graph_load_task.c \
|
||||||
|
|
@ -614,7 +620,8 @@ test: \
|
||||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||||
$(TEST_OSINT_SELECTION_CONTEXT) \
|
$(TEST_OSINT_SELECTION_CONTEXT) \
|
||||||
$(TEST_OSINT_ACTION_CATALOG)
|
$(TEST_OSINT_ACTION_CATALOG) \
|
||||||
|
$(TEST_OSINT_DNS_QUERY)
|
||||||
@echo "Exécution des tests..."
|
@echo "Exécution des tests..."
|
||||||
@./$(TEST_NODE)
|
@./$(TEST_NODE)
|
||||||
@./$(TEST_TREE_MODEL)
|
@./$(TEST_TREE_MODEL)
|
||||||
|
|
@ -664,6 +671,7 @@ test: \
|
||||||
@$(TEST_GRAPH_NODE_POSITION_DAO)
|
@$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||||
@$(TEST_OSINT_SELECTION_CONTEXT)
|
@$(TEST_OSINT_SELECTION_CONTEXT)
|
||||||
@$(TEST_OSINT_ACTION_CATALOG)
|
@$(TEST_OSINT_ACTION_CATALOG)
|
||||||
|
@$(TEST_OSINT_DNS_QUERY)
|
||||||
@echo "Tous les tests sont valides."
|
@echo "Tous les tests sont valides."
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|
@ -719,7 +727,8 @@ clean:
|
||||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||||
$(TEST_OSINT_SELECTION_CONTEXT) \
|
$(TEST_OSINT_SELECTION_CONTEXT) \
|
||||||
$(TEST_OSINT_ACTION_CATALOG)
|
$(TEST_OSINT_ACTION_CATALOG) \
|
||||||
|
$(TEST_OSINT_DNS_QUERY)
|
||||||
|
|
||||||
-include $(DEP)
|
-include $(DEP)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,9 @@ Le socle actuel comprend notamment :
|
||||||
- menu OSINT contextuel préparé pour les entités et relations du graphe ;
|
- menu OSINT contextuel préparé pour les entités et relations du graphe ;
|
||||||
- contexte de sélection OSINT indépendant de GTK et validé par des tests ;
|
- contexte de sélection OSINT indépendant de GTK et validé par des tests ;
|
||||||
- catalogue déterministe des actions compatibles avec la sélection OSINT ;
|
- catalogue déterministe des actions compatibles avec la sélection OSINT ;
|
||||||
- disponibilité des actions OSINT synchronisée avec le registre d’outils.
|
- disponibilité des actions OSINT synchronisée avec le registre d’outils ;
|
||||||
|
- résolution DNS asynchrone avec `dig` depuis une entité domaine ;
|
||||||
|
- affichage sélectionnable des sorties standard et d’erreur, sans persistance.
|
||||||
|
|
||||||
Les outils actuellement présents dans le catalogue initial sont :
|
Les outils actuellement présents dans le catalogue initial sont :
|
||||||
|
|
||||||
|
|
|
||||||
30
include/models/osint_dns_query.h
Normal file
30
include/models/osint_dns_query.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file osint_dns_query.h
|
||||||
|
* @brief Validation des cibles utilisées pour une résolution DNS OSINT.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LABFY_INVESTIGATION_OSINT_DNS_QUERY_H
|
||||||
|
#define LABFY_INVESTIGATION_OSINT_DNS_QUERY_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie qu'une cible est un nom de domaine DNS exploitable.
|
||||||
|
*
|
||||||
|
* La fonction accepte les lettres ASCII sans distinction de casse, les
|
||||||
|
* chiffres, les tirets internes et les points séparant les labels. Un point
|
||||||
|
* final représentant la racine DNS est également accepté.
|
||||||
|
*
|
||||||
|
* @param target_value Valeur à vérifier.
|
||||||
|
*
|
||||||
|
* @return TRUE lorsque la cible peut être transmise à dig.
|
||||||
|
*/
|
||||||
|
gboolean osint_dns_query_is_valid_target(
|
||||||
|
const char *target_value
|
||||||
|
);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -49,6 +49,26 @@ void application_message_dialog_present(
|
||||||
const char *message
|
const char *message
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Affiche un message accompagné d'un contenu textuel défilable.
|
||||||
|
*
|
||||||
|
* Le contenu détaillé est affiché dans une zone monospace sélectionnable.
|
||||||
|
* Les chaînes sont copiées par les widgets GTK.
|
||||||
|
*
|
||||||
|
* @param parent_window Fenêtre parente, ou NULL.
|
||||||
|
* @param message_type Type de message.
|
||||||
|
* @param title Titre de la fenêtre, ou NULL.
|
||||||
|
* @param message Message principal, ou NULL.
|
||||||
|
* @param details Contenu détaillé, ou NULL.
|
||||||
|
*/
|
||||||
|
void application_message_dialog_present_details(
|
||||||
|
GtkWindow *parent_window,
|
||||||
|
ApplicationMessageDialogType message_type,
|
||||||
|
const char *title,
|
||||||
|
const char *message,
|
||||||
|
const char *details
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Affiche un dialogue modal demandant une confirmation.
|
* @brief Affiche un dialogue modal demandant une confirmation.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,19 @@ typedef void (*MainWindowAddRelationCallback)(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback appelé lors du déclenchement d'une action OSINT.
|
||||||
|
*
|
||||||
|
* @param action_identifier Identifiant stable de l'action.
|
||||||
|
* @param target_value Valeur métier ciblée.
|
||||||
|
* @param user_data Données privées fournies par l'appelant.
|
||||||
|
*/
|
||||||
|
typedef void (*MainWindowOsintActionCallback)(
|
||||||
|
const char *action_identifier,
|
||||||
|
const char *target_value,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Définit le callback de vérification d'une preuve.
|
* @brief Définit le callback de vérification d'une preuve.
|
||||||
*
|
*
|
||||||
|
|
@ -181,6 +194,21 @@ void main_window_set_add_relation_callback(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Définit le callback de déclenchement des actions OSINT.
|
||||||
|
*
|
||||||
|
* MainWindow relaie uniquement la demande provenant du Workspace.
|
||||||
|
*
|
||||||
|
* @param main_window Fenêtre principale.
|
||||||
|
* @param callback Callback facultatif.
|
||||||
|
* @param user_data Données privées transmises au callback.
|
||||||
|
*/
|
||||||
|
void main_window_set_osint_action_callback(
|
||||||
|
MainWindow *main_window,
|
||||||
|
MainWindowOsintActionCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Définit le callback du bouton « Nouvelle enquête ».
|
* @brief Définit le callback du bouton « Nouvelle enquête ».
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,21 @@ typedef void (*WorkspaceAddRelationCallback)(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback appelé lors du déclenchement d'une action OSINT.
|
||||||
|
*
|
||||||
|
* Les chaînes sont empruntées et uniquement valides pendant l'appel.
|
||||||
|
*
|
||||||
|
* @param action_identifier Identifiant stable de l'action.
|
||||||
|
* @param target_value Valeur métier ciblée par l'action.
|
||||||
|
* @param user_data Données empruntées fournies par l'appelant.
|
||||||
|
*/
|
||||||
|
typedef void (*WorkspaceOsintActionCallback)(
|
||||||
|
const char *action_identifier,
|
||||||
|
const char *target_value,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Crée une nouvelle zone de travail.
|
* @brief Crée une nouvelle zone de travail.
|
||||||
*
|
*
|
||||||
|
|
@ -171,6 +186,22 @@ void workspace_set_osint_tool_state(
|
||||||
const char *version
|
const char *version
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Définit le callback de déclenchement des actions OSINT.
|
||||||
|
*
|
||||||
|
* Le Workspace ne lance aucun outil externe. Il relaie uniquement l'action
|
||||||
|
* et la valeur du contexte courant vers la couche applicative.
|
||||||
|
*
|
||||||
|
* @param workspace Zone de travail.
|
||||||
|
* @param callback Callback facultatif.
|
||||||
|
* @param user_data Données empruntées transmises au callback.
|
||||||
|
*/
|
||||||
|
void workspace_set_osint_action_callback(
|
||||||
|
Workspace *workspace,
|
||||||
|
WorkspaceOsintActionCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Définit le callback de vérification de la preuve affichée.
|
* @brief Définit le callback de vérification de la preuve affichée.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "models/investigation_graph_layout.h"
|
#include "models/investigation_graph_layout.h"
|
||||||
#include "models/investigation_graph_model.h"
|
#include "models/investigation_graph_model.h"
|
||||||
#include "models/investigation_record.h"
|
#include "models/investigation_record.h"
|
||||||
|
#include "models/osint_dns_query.h"
|
||||||
#include "core/investigation_tree_builder.h"
|
#include "core/investigation_tree_builder.h"
|
||||||
#include "views/create_investigation_dialog.h"
|
#include "views/create_investigation_dialog.h"
|
||||||
#include "views/folder_dialog.h"
|
#include "views/folder_dialog.h"
|
||||||
|
|
@ -21,6 +22,7 @@
|
||||||
#include "core/task_manager.h"
|
#include "core/task_manager.h"
|
||||||
#include "core/background_task.h"
|
#include "core/background_task.h"
|
||||||
#include "core/tool_initializer.h"
|
#include "core/tool_initializer.h"
|
||||||
|
#include "core/tool_task.h"
|
||||||
#include "views/file_dialog.h"
|
#include "views/file_dialog.h"
|
||||||
#include "core/evidence_import_task.h"
|
#include "core/evidence_import_task.h"
|
||||||
#include "models/evidence_record.h"
|
#include "models/evidence_record.h"
|
||||||
|
|
@ -151,6 +153,33 @@ typedef struct
|
||||||
char *original_name;
|
char *original_name;
|
||||||
} ApplicationEvidenceIntegrityContext;
|
} ApplicationEvidenceIntegrityContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Contexte conservé jusqu'à la fin d'une action OSINT.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Application *application;
|
||||||
|
char *target_value;
|
||||||
|
} ApplicationOsintActionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Libère le contexte d'une action OSINT.
|
||||||
|
*/
|
||||||
|
static void application_osint_action_context_free(
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ApplicationOsintActionContext *context = user_data;
|
||||||
|
|
||||||
|
if (context == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(context->target_value);
|
||||||
|
g_free(context);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Libère le contexte du dialogue de métadonnées.
|
* @brief Libère le contexte du dialogue de métadonnées.
|
||||||
*/
|
*/
|
||||||
|
|
@ -914,6 +943,261 @@ static void application_start_tool_initialization(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convertit une sortie binaire d'outil en texte UTF-8 affichable.
|
||||||
|
*
|
||||||
|
* @param bytes Octets empruntés, ou NULL.
|
||||||
|
*
|
||||||
|
* @return Nouvelle chaîne UTF-8, jamais NULL.
|
||||||
|
*/
|
||||||
|
static char *application_osint_output_to_utf8(
|
||||||
|
GBytes *bytes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
gconstpointer data = NULL;
|
||||||
|
gsize data_size = 0;
|
||||||
|
|
||||||
|
if (bytes == NULL)
|
||||||
|
{
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
data = g_bytes_get_data(bytes, &data_size);
|
||||||
|
if (data == NULL || data_size == 0U)
|
||||||
|
{
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_utf8_make_valid(data, (gssize) data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Présente le résultat final d'une résolution DNS.
|
||||||
|
*/
|
||||||
|
static void application_on_dns_lookup_completed(
|
||||||
|
BackgroundTask *task,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ApplicationOsintActionContext *context = user_data;
|
||||||
|
Application *application = NULL;
|
||||||
|
const ToolTaskResult *task_result = NULL;
|
||||||
|
const ToolProcessResult *process_result = NULL;
|
||||||
|
GBytes *stdout_bytes = NULL;
|
||||||
|
GBytes *stderr_bytes = NULL;
|
||||||
|
char *stdout_text = NULL;
|
||||||
|
char *stderr_text = NULL;
|
||||||
|
char *details = NULL;
|
||||||
|
char *message = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (task == NULL || context == NULL || context->application == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
application = context->application;
|
||||||
|
if (application->main_window == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (background_task_get_state(task) != BACKGROUND_TASK_STATE_COMPLETED)
|
||||||
|
{
|
||||||
|
error = background_task_dup_error(task);
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS impossible",
|
||||||
|
error != NULL
|
||||||
|
? error->message
|
||||||
|
: "L'exécution de dig a été annulée ou a échoué."
|
||||||
|
);
|
||||||
|
g_clear_error(&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
task_result = tool_task_result_from_background_task(task);
|
||||||
|
process_result = tool_task_result_get_process_result(task_result);
|
||||||
|
if (process_result == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS incomplète",
|
||||||
|
"La tâche s'est terminée sans résultat exploitable."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout_bytes = tool_process_result_ref_stdout(process_result);
|
||||||
|
stderr_bytes = tool_process_result_ref_stderr(process_result);
|
||||||
|
stdout_text = application_osint_output_to_utf8(stdout_bytes);
|
||||||
|
stderr_text = application_osint_output_to_utf8(stderr_bytes);
|
||||||
|
|
||||||
|
if (stdout_text != NULL && stdout_text[0] != '\0' &&
|
||||||
|
stderr_text != NULL && stderr_text[0] != '\0')
|
||||||
|
{
|
||||||
|
details = g_strdup_printf(
|
||||||
|
"Sortie standard :\n%s\n\nSortie d'erreur :\n%s",
|
||||||
|
stdout_text,
|
||||||
|
stderr_text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (stdout_text != NULL && stdout_text[0] != '\0')
|
||||||
|
{
|
||||||
|
details = g_strdup(stdout_text);
|
||||||
|
}
|
||||||
|
else if (stderr_text != NULL && stderr_text[0] != '\0')
|
||||||
|
{
|
||||||
|
details = g_strdup(stderr_text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
details = g_strdup("Aucun enregistrement DNS retourné.");
|
||||||
|
}
|
||||||
|
|
||||||
|
message = g_strdup_printf(
|
||||||
|
"Cible : %s — code de sortie : %d",
|
||||||
|
context->target_value,
|
||||||
|
tool_process_result_get_exit_status(process_result)
|
||||||
|
);
|
||||||
|
|
||||||
|
application_message_dialog_present_details(
|
||||||
|
main_window_get_window(application->main_window),
|
||||||
|
tool_process_result_is_success(process_result)
|
||||||
|
? APPLICATION_MESSAGE_DIALOG_INFORMATION
|
||||||
|
: APPLICATION_MESSAGE_DIALOG_WARNING,
|
||||||
|
"Résultat de la résolution DNS",
|
||||||
|
message,
|
||||||
|
details
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_pointer(&stdout_bytes, g_bytes_unref);
|
||||||
|
g_clear_pointer(&stderr_bytes, g_bytes_unref);
|
||||||
|
g_free(stdout_text);
|
||||||
|
g_free(stderr_text);
|
||||||
|
g_free(details);
|
||||||
|
g_free(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prépare et démarre une résolution DNS avec dig.
|
||||||
|
*/
|
||||||
|
static void application_start_dns_lookup(
|
||||||
|
Application *application,
|
||||||
|
const char *target_value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ToolRegistry *tool_registry = NULL;
|
||||||
|
ToolTask *tool_task = NULL;
|
||||||
|
BackgroundTask *background_task = NULL;
|
||||||
|
ApplicationOsintActionContext *context = NULL;
|
||||||
|
const char *arguments[] = {"+noall", "+answer", target_value, NULL};
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (application == NULL || application->task_manager == NULL ||
|
||||||
|
application->tool_initializer == NULL || target_value == NULL ||
|
||||||
|
!osint_dns_query_is_valid_target(target_value))
|
||||||
|
{
|
||||||
|
if (application != NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS impossible",
|
||||||
|
"Le nom de domaine sélectionné est invalide."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tool_registry = tool_initializer_get_registry(application->tool_initializer);
|
||||||
|
tool_task = tool_task_new(
|
||||||
|
tool_registry,
|
||||||
|
"dns.dig",
|
||||||
|
"Résolution DNS",
|
||||||
|
arguments,
|
||||||
|
NULL,
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tool_task == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS impossible",
|
||||||
|
error != NULL ? error->message : "Impossible de préparer dig."
|
||||||
|
);
|
||||||
|
g_clear_error(&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = g_try_new0(ApplicationOsintActionContext, 1);
|
||||||
|
if (context != NULL)
|
||||||
|
{
|
||||||
|
context->application = application;
|
||||||
|
context->target_value = g_strdup(target_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context == NULL || context->target_value == NULL)
|
||||||
|
{
|
||||||
|
application_osint_action_context_free(context);
|
||||||
|
tool_task_free(tool_task);
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS impossible",
|
||||||
|
"Impossible de conserver le contexte de l'action."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
background_task = tool_task_get_background_task(tool_task);
|
||||||
|
if (!task_manager_add(application->task_manager, background_task, &error) ||
|
||||||
|
!tool_task_start(
|
||||||
|
tool_task,
|
||||||
|
application_on_dns_lookup_completed,
|
||||||
|
context,
|
||||||
|
application_osint_action_context_free,
|
||||||
|
&error
|
||||||
|
))
|
||||||
|
{
|
||||||
|
task_manager_remove(application->task_manager, background_task);
|
||||||
|
application_osint_action_context_free(context);
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Résolution DNS impossible",
|
||||||
|
error != NULL ? error->message : "Impossible de démarrer dig."
|
||||||
|
);
|
||||||
|
g_clear_error(&error);
|
||||||
|
tool_task_free(tool_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tool_task_free(tool_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Traite une action OSINT relayée par la fenêtre principale.
|
||||||
|
*/
|
||||||
|
static void application_on_osint_action_requested(
|
||||||
|
const char *action_identifier,
|
||||||
|
const char *target_value,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Application *application = user_data;
|
||||||
|
|
||||||
|
if (g_strcmp0(action_identifier, "dns-preview") == 0)
|
||||||
|
{
|
||||||
|
application_start_dns_lookup(application, target_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Action OSINT inconnue",
|
||||||
|
"Cette action n'est pas encore prise en charge."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Affiche une erreur dans une fenêtre GTK.
|
* @brief Affiche une erreur dans une fenêtre GTK.
|
||||||
*
|
*
|
||||||
|
|
@ -5102,6 +5386,12 @@ static void application_on_activate(
|
||||||
application
|
application
|
||||||
);
|
);
|
||||||
|
|
||||||
|
main_window_set_osint_action_callback(
|
||||||
|
application->main_window,
|
||||||
|
application_on_osint_action_requested,
|
||||||
|
application
|
||||||
|
);
|
||||||
|
|
||||||
main_window_set_new_investigation_callback(
|
main_window_set_new_investigation_callback(
|
||||||
application->main_window,
|
application->main_window,
|
||||||
application_on_new_investigation_requested,
|
application_on_new_investigation_requested,
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ OsintActionCatalog *osint_action_catalog_new_defaults(void)
|
||||||
);
|
);
|
||||||
dns_action = osint_action_new(
|
dns_action = osint_action_new(
|
||||||
"dns-preview", "Résolution DNS",
|
"dns-preview", "Résolution DNS",
|
||||||
"Préfiguration de l'adaptateur DNS.",
|
"Exécute dig sur le nom de domaine sélectionné.",
|
||||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY, "domain_name", "dns.dig", FALSE,
|
OSINT_SELECTION_CONTEXT_KIND_ENTITY, "domain_name", "dns.dig", FALSE,
|
||||||
"L'outil requis n'a pas encore été vérifié."
|
"L'outil requis n'a pas encore été vérifié."
|
||||||
);
|
);
|
||||||
|
|
|
||||||
70
src/models/osint_dns_query.c
Normal file
70
src/models/osint_dns_query.c
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file osint_dns_query.c
|
||||||
|
* @brief Validation des cibles utilisées pour une résolution DNS OSINT.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "models/osint_dns_query.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
gboolean osint_dns_query_is_valid_target(
|
||||||
|
const char *target_value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
gsize target_length = 0;
|
||||||
|
gsize label_length = 0;
|
||||||
|
|
||||||
|
if (target_value == NULL || target_value[0] == '\0')
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_length = strlen(target_value);
|
||||||
|
if (target_length > 253U)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (gsize character_index = 0;
|
||||||
|
character_index < target_length;
|
||||||
|
character_index++)
|
||||||
|
{
|
||||||
|
const char character = target_value[character_index];
|
||||||
|
|
||||||
|
if (character == '.')
|
||||||
|
{
|
||||||
|
if (label_length == 0U)
|
||||||
|
{
|
||||||
|
return character_index == target_length - 1U &&
|
||||||
|
character_index > 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_value[character_index - 1U] == '-')
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character_index == target_length - 1U)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_length = 0U;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(g_ascii_isalnum(character) || character == '-'))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((label_length == 0U && character == '-') || label_length >= 63U)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return label_length > 0U && target_value[target_length - 1U] != '-';
|
||||||
|
}
|
||||||
|
|
@ -355,6 +355,93 @@ void application_message_dialog_present(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void application_message_dialog_present_details(
|
||||||
|
GtkWindow *parent_window,
|
||||||
|
ApplicationMessageDialogType message_type,
|
||||||
|
const char *title,
|
||||||
|
const char *message,
|
||||||
|
const char *details
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkWindow *dialog_window = GTK_WINDOW(gtk_window_new());
|
||||||
|
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
|
||||||
|
GtkWidget *title_label = gtk_label_new(
|
||||||
|
application_message_dialog_get_safe_title(message_type, title)
|
||||||
|
);
|
||||||
|
GtkWidget *message_label = gtk_label_new(
|
||||||
|
application_message_dialog_get_safe_message(message)
|
||||||
|
);
|
||||||
|
GtkWidget *scrolled_window = gtk_scrolled_window_new();
|
||||||
|
GtkWidget *details_view = gtk_text_view_new();
|
||||||
|
GtkWidget *close_button = gtk_button_new_with_label("Fermer");
|
||||||
|
GtkTextBuffer *details_buffer = gtk_text_view_get_buffer(
|
||||||
|
GTK_TEXT_VIEW(details_view)
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_window_set_title(
|
||||||
|
dialog_window,
|
||||||
|
application_message_dialog_get_safe_title(message_type, title)
|
||||||
|
);
|
||||||
|
gtk_window_set_default_size(dialog_window, 760, 520);
|
||||||
|
gtk_window_set_modal(dialog_window, TRUE);
|
||||||
|
gtk_window_set_destroy_with_parent(dialog_window, TRUE);
|
||||||
|
if (parent_window != NULL)
|
||||||
|
{
|
||||||
|
gtk_window_set_transient_for(dialog_window, parent_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_set_margin_start(main_box, 20);
|
||||||
|
gtk_widget_set_margin_end(main_box, 20);
|
||||||
|
gtk_widget_set_margin_top(main_box, 20);
|
||||||
|
gtk_widget_set_margin_bottom(main_box, 20);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(title_label, GTK_ALIGN_START);
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(title_label), 0.0F);
|
||||||
|
gtk_widget_add_css_class(title_label, "title-2");
|
||||||
|
|
||||||
|
gtk_widget_set_halign(message_label, GTK_ALIGN_FILL);
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(message_label), 0.0F);
|
||||||
|
gtk_label_set_wrap(GTK_LABEL(message_label), TRUE);
|
||||||
|
|
||||||
|
gtk_text_view_set_editable(GTK_TEXT_VIEW(details_view), FALSE);
|
||||||
|
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(details_view), TRUE);
|
||||||
|
gtk_text_view_set_monospace(GTK_TEXT_VIEW(details_view), TRUE);
|
||||||
|
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(details_view), GTK_WRAP_NONE);
|
||||||
|
gtk_text_buffer_set_text(
|
||||||
|
details_buffer,
|
||||||
|
details != NULL && details[0] != '\0'
|
||||||
|
? details
|
||||||
|
: APPLICATION_MESSAGE_DIALOG_DEFAULT_MESSAGE,
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_scrolled_window_set_policy(
|
||||||
|
GTK_SCROLLED_WINDOW(scrolled_window),
|
||||||
|
GTK_POLICY_AUTOMATIC,
|
||||||
|
GTK_POLICY_AUTOMATIC
|
||||||
|
);
|
||||||
|
gtk_widget_set_vexpand(scrolled_window, TRUE);
|
||||||
|
gtk_scrolled_window_set_child(
|
||||||
|
GTK_SCROLLED_WINDOW(scrolled_window),
|
||||||
|
details_view
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(close_button, GTK_ALIGN_END);
|
||||||
|
g_signal_connect(
|
||||||
|
close_button,
|
||||||
|
"clicked",
|
||||||
|
G_CALLBACK(application_message_dialog_on_close_clicked),
|
||||||
|
dialog_window
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(main_box), title_label);
|
||||||
|
gtk_box_append(GTK_BOX(main_box), message_label);
|
||||||
|
gtk_box_append(GTK_BOX(main_box), scrolled_window);
|
||||||
|
gtk_box_append(GTK_BOX(main_box), close_button);
|
||||||
|
gtk_window_set_child(dialog_window, main_box);
|
||||||
|
gtk_window_present(dialog_window);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Libère le contexte associé à une fenêtre de confirmation.
|
* @brief Libère le contexte associé à une fenêtre de confirmation.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,12 @@ struct MainWindow
|
||||||
gpointer
|
gpointer
|
||||||
add_relation_user_data;
|
add_relation_user_data;
|
||||||
|
|
||||||
|
MainWindowOsintActionCallback
|
||||||
|
osint_action_callback;
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
osint_action_user_data;
|
||||||
|
|
||||||
MainWindowQuitCallback
|
MainWindowQuitCallback
|
||||||
quit_callback;
|
quit_callback;
|
||||||
|
|
||||||
|
|
@ -125,6 +131,29 @@ struct MainWindow
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Relaie une action OSINT vers le contrôleur applicatif.
|
||||||
|
*/
|
||||||
|
static void main_window_on_osint_action_requested(
|
||||||
|
const char *action_identifier,
|
||||||
|
const char *target_value,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
MainWindow *main_window = user_data;
|
||||||
|
|
||||||
|
if (main_window == NULL || main_window->osint_action_callback == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
main_window->osint_action_callback(
|
||||||
|
action_identifier,
|
||||||
|
target_value,
|
||||||
|
main_window->osint_action_user_data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ouvre dans le workspace l'entité choisie dans la sidebar.
|
* @brief Ouvre dans le workspace l'entité choisie dans la sidebar.
|
||||||
*/
|
*/
|
||||||
|
|
@ -745,6 +774,12 @@ MainWindow *main_window_new(
|
||||||
main_window
|
main_window
|
||||||
);
|
);
|
||||||
|
|
||||||
|
workspace_set_osint_action_callback(
|
||||||
|
main_window->workspace,
|
||||||
|
main_window_on_osint_action_requested,
|
||||||
|
main_window
|
||||||
|
);
|
||||||
|
|
||||||
workspace_widget = workspace_get_widget(
|
workspace_widget = workspace_get_widget(
|
||||||
main_window->workspace
|
main_window->workspace
|
||||||
);
|
);
|
||||||
|
|
@ -1245,6 +1280,21 @@ void main_window_set_osint_tool_state(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void main_window_set_osint_action_callback(
|
||||||
|
MainWindow *main_window,
|
||||||
|
MainWindowOsintActionCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (main_window == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
main_window->osint_action_callback = callback;
|
||||||
|
main_window->osint_action_user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
void main_window_set_tree_selection_callback(
|
void main_window_set_tree_selection_callback(
|
||||||
MainWindow *main_window,
|
MainWindow *main_window,
|
||||||
InvestigationTreeViewSelectionCallback callback,
|
InvestigationTreeViewSelectionCallback callback,
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,12 @@ struct Workspace
|
||||||
gpointer
|
gpointer
|
||||||
add_relation_user_data;
|
add_relation_user_data;
|
||||||
|
|
||||||
|
WorkspaceOsintActionCallback
|
||||||
|
osint_action_callback;
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
osint_action_user_data;
|
||||||
|
|
||||||
GtkWidget *node_name_label;
|
GtkWidget *node_name_label;
|
||||||
GtkWidget *node_path_label;
|
GtkWidget *node_path_label;
|
||||||
GtkWidget *node_type_label;
|
GtkWidget *node_type_label;
|
||||||
|
|
@ -128,34 +134,62 @@ struct Workspace
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Confirme localement le déclenchement de l'action de démonstration.
|
* @brief Traite localement l'aperçu ou relaie une action OSINT exécutable.
|
||||||
*/
|
*/
|
||||||
static void workspace_on_osint_demo_action_clicked(
|
static void workspace_on_osint_action_clicked(
|
||||||
GtkButton *button,
|
GtkButton *button,
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Workspace *workspace = user_data;
|
Workspace *workspace = user_data;
|
||||||
const char *action_label = g_object_get_data(
|
const char *action_identifier = g_object_get_data(
|
||||||
G_OBJECT(button),
|
G_OBJECT(button),
|
||||||
"osint-action-label"
|
"osint-action-identifier"
|
||||||
);
|
);
|
||||||
char *status_text = NULL;
|
const char *target_value = NULL;
|
||||||
|
|
||||||
if (workspace == NULL || workspace->osint_tools_status_label == NULL)
|
if (workspace == NULL || workspace->osint_tools_status_label == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_text = g_strdup_printf(
|
if (g_strcmp0(action_identifier, "selection-preview") == 0)
|
||||||
"%s : démonstration locale, aucun outil exécuté.",
|
{
|
||||||
action_label != NULL ? action_label : "Action OSINT"
|
|
||||||
);
|
|
||||||
gtk_label_set_text(
|
gtk_label_set_text(
|
||||||
GTK_LABEL(workspace->osint_tools_status_label),
|
GTK_LABEL(workspace->osint_tools_status_label),
|
||||||
status_text != NULL ? status_text : "Aucun outil exécuté."
|
"Aperçu local : aucun outil externe exécuté."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_value = osint_selection_context_get_value(
|
||||||
|
workspace->osint_selection_context
|
||||||
|
);
|
||||||
|
|
||||||
|
if (workspace->osint_action_callback == NULL ||
|
||||||
|
action_identifier == NULL || target_value == NULL)
|
||||||
|
{
|
||||||
|
gtk_label_set_text(
|
||||||
|
GTK_LABEL(workspace->osint_tools_status_label),
|
||||||
|
"Impossible de transmettre cette action OSINT."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_label_set_text(
|
||||||
|
GTK_LABEL(workspace->osint_tools_status_label),
|
||||||
|
"Exécution lancée ; suivez sa progression dans les tâches."
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_popover_popdown(
|
||||||
|
GTK_POPOVER(workspace->osint_tools_popover)
|
||||||
|
);
|
||||||
|
|
||||||
|
workspace->osint_action_callback(
|
||||||
|
action_identifier,
|
||||||
|
target_value,
|
||||||
|
workspace->osint_action_user_data
|
||||||
);
|
);
|
||||||
g_free(status_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -282,15 +316,16 @@ static void workspace_update_osint_tools_menu(
|
||||||
? osint_action_get_description(action)
|
? osint_action_get_description(action)
|
||||||
: osint_action_get_unavailable_reason(action)
|
: osint_action_get_unavailable_reason(action)
|
||||||
);
|
);
|
||||||
g_object_set_data(
|
g_object_set_data_full(
|
||||||
G_OBJECT(action_button),
|
G_OBJECT(action_button),
|
||||||
"osint-action-label",
|
"osint-action-identifier",
|
||||||
(gpointer) osint_action_get_label(action)
|
g_strdup(osint_action_get_identifier(action)),
|
||||||
|
g_free
|
||||||
);
|
);
|
||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
action_button,
|
action_button,
|
||||||
"clicked",
|
"clicked",
|
||||||
G_CALLBACK(workspace_on_osint_demo_action_clicked),
|
G_CALLBACK(workspace_on_osint_action_clicked),
|
||||||
workspace
|
workspace
|
||||||
);
|
);
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
|
|
@ -2352,6 +2387,21 @@ void workspace_set_osint_tool_state(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void workspace_set_osint_action_callback(
|
||||||
|
Workspace *workspace,
|
||||||
|
WorkspaceOsintActionCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (workspace == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace->osint_action_callback = callback;
|
||||||
|
workspace->osint_action_user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
void workspace_set_graph_loading(
|
void workspace_set_graph_loading(
|
||||||
Workspace *workspace
|
Workspace *workspace
|
||||||
)
|
)
|
||||||
|
|
|
||||||
35
tests/test_osint_dns_query.c
Normal file
35
tests/test_osint_dns_query.c
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file test_osint_dns_query.c
|
||||||
|
* @brief Tests de validation des cibles de résolution DNS OSINT.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "models/osint_dns_query.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
static void test_valid_targets(void)
|
||||||
|
{
|
||||||
|
g_assert_true(osint_dns_query_is_valid_target("example.org"));
|
||||||
|
g_assert_true(osint_dns_query_is_valid_target("WWW.Example.ORG"));
|
||||||
|
g_assert_true(osint_dns_query_is_valid_target("sub-domain.example.org"));
|
||||||
|
g_assert_true(osint_dns_query_is_valid_target("example.org."));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_invalid_targets(void)
|
||||||
|
{
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target(NULL));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target(""));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target("-x.example.org"));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target("example..org"));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target("example.org +trace"));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target("example_.org"));
|
||||||
|
g_assert_false(osint_dns_query_is_valid_target("example-.org"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
g_test_add_func("/osint-dns-query/valid", test_valid_targets);
|
||||||
|
g_test_add_func("/osint-dns-query/invalid", test_invalid_targets);
|
||||||
|
return g_test_run();
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue