From 3b8c06b86d479be1e6e305fc8aec928e3ce795ac Mon Sep 17 00:00:00 2001 From: toto Date: Tue, 7 Apr 2026 21:35:19 +0200 Subject: [PATCH] docs: add Doxygen comments to mod_reqin_log.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - File header: French multi-line description block - 7 section banners in French (/* ====== Section ====== */ format): Configuration du serveur, Buffer dynamique, Sérialisation JSON, Gestionnaires de directives, Socket Unix, Journalisation, Hooks Apache - 26 @brief/@param/@return blocks on every function: server config, dynbuf_*, JSON helpers, cmd_set_* handlers, socket helpers (try_connect/ensure_connected/write_to_socket), log_request, Apache hooks (post_read_request, child_init, etc.) - No logic changes (1033 → 1268 lines, comments only) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- services/mod-reqin-log/src/mod_reqin_log.c | 259 ++++++++++++++++++++- 1 file changed, 247 insertions(+), 12 deletions(-) diff --git a/services/mod-reqin-log/src/mod_reqin_log.c b/services/mod-reqin-log/src/mod_reqin_log.c index 6c91785..eb39fde 100644 --- a/services/mod-reqin-log/src/mod_reqin_log.c +++ b/services/mod-reqin-log/src/mod_reqin_log.c @@ -1,5 +1,9 @@ -/* - * mod_reqin_log.c - Apache HTTPD module for logging HTTP requests as JSON to Unix socket +/* mod_reqin_log.c — Module Apache HTTPD pour la journalisation des requêtes HTTP + * entrantes au format JSON vers un socket de domaine Unix. + * + * Fonctionnalités : capture des requêtes en phase post-read, sérialisation JSON, + * envoi non-bloquant vers un socket Unix, reconnexion automatique et filtrage + * des en-têtes sensibles. * * Copyright (c) 2026. All rights reserved. */ @@ -161,13 +165,26 @@ module AP_MODULE_DECLARE_DATA reqin_log_module = { 0 /* flags */ }; -/* Get module configuration */ +/* ====== Configuration du serveur ====== */ + +/** + * @brief Retourne la configuration du module pour un serveur virtuel donné. + * + * @param s Enregistrement du serveur Apache. + * @return Pointeur vers la configuration du serveur, ou NULL. + */ static reqin_log_server_conf_t *get_server_conf(server_rec *s) { return (reqin_log_server_conf_t *)ap_get_module_config(s->module_config, &reqin_log_module); } -/* Create server configuration */ +/** + * @brief Crée et initialise la configuration du serveur avec les valeurs par défaut. + * + * @param pool Pool APR pour les allocations mémoire. + * @param s Enregistrement du serveur (non utilisé). + * @return Pointeur opaque vers la configuration initialisée. + */ static void *reqin_log_create_server_conf(apr_pool_t *pool, server_rec *s) { (void)s; @@ -191,8 +208,15 @@ static void *reqin_log_create_server_conf(apr_pool_t *pool, server_rec *s) return srv_conf; } -/* ============== Dynamic Buffer Functions ============== */ +/* ====== Fonctions de buffer dynamique ====== */ +/** + * @brief Initialise le buffer dynamique avec la capacité initiale spécifiée. + * + * @param db Pointeur vers le buffer dynamique à initialiser. + * @param pool Pool APR utilisé pour toutes les allocations mémoire. + * @param initial_capacity Capacité initiale en octets. + */ static void dynbuf_init(dynbuf_t *db, apr_pool_t *pool, apr_size_t initial_capacity) { db->pool = pool; @@ -202,6 +226,13 @@ static void dynbuf_init(dynbuf_t *db, apr_pool_t *pool, apr_size_t initial_capac db->data[0] = '\0'; } +/** + * @brief Ajoute une chaîne au buffer dynamique, en le redimensionnant si nécessaire. + * + * @param db Pointeur vers le buffer dynamique. + * @param str Chaîne à ajouter (ignorée si NULL). + * @param len Nombre d'octets à copier, ou (apr_size_t)-1 pour utiliser strlen(str). + */ static void dynbuf_append(dynbuf_t *db, const char *str, apr_size_t len) { if (str == NULL) return; @@ -223,6 +254,12 @@ static void dynbuf_append(dynbuf_t *db, const char *str, apr_size_t len) db->data[db->len] = '\0'; } +/** + * @brief Ajoute un seul caractère au buffer dynamique, en le redimensionnant si nécessaire. + * + * @param db Pointeur vers le buffer dynamique. + * @param c Caractère à ajouter. + */ static void dynbuf_append_char(dynbuf_t *db, char c) { if (db->len + 1 >= db->capacity) { @@ -236,8 +273,17 @@ static void dynbuf_append_char(dynbuf_t *db, char c) db->data[db->len] = '\0'; } -/* ============== JSON Helper Functions ============== */ +/* ====== Sérialisation JSON ====== */ +/** + * @brief Échappe et ajoute une chaîne C dans le buffer au format JSON (RFC 7159). + * + * Les caractères spéciaux (guillemets, antislash, retours chariot, etc.) sont encodés. + * Les caractères de contrôle inférieurs à 0x20 sont produits sous la forme \\uXXXX. + * + * @param db Pointeur vers le buffer dynamique de destination. + * @param str Chaîne source à sérialiser (ignorée si NULL). + */ static void append_json_string(dynbuf_t *db, const char *str) { if (str == NULL) { @@ -267,6 +313,15 @@ static void append_json_string(dynbuf_t *db, const char *str) } } +/** + * @brief Formate un horodatage APR en chaîne ISO 8601 UTC et l'ajoute au buffer. + * + * Le format produit est YYYY-MM-DDTHH:MM:SSZ (20 caractères). + * Les composantes hors plage sont écrêtées pour éviter tout dépassement de tampon. + * + * @param db Pointeur vers le buffer dynamique de destination. + * @param t Horodatage APR en microsecondes depuis l'époque Unix. + */ static void format_iso8601(dynbuf_t *db, apr_time_t t) { apr_time_exp_t tm; @@ -302,6 +357,16 @@ static void format_iso8601(dynbuf_t *db, apr_time_t t) dynbuf_append(db, time_str, -1); } +/** + * @brief Analyse strictement un entier décimal depuis une chaîne de caractères. + * + * Rejette toute chaîne contenant des espaces en tête, des caractères non numériques + * ou des valeurs hors de la plage [INT_MIN, INT_MAX]. + * + * @param arg Chaîne à analyser. + * @param out Pointeur vers l'entier résultat (renseigné en cas de succès). + * @return 0 en cas de succès, -1 en cas d'erreur. + */ static int parse_int_strict(const char *arg, int *out) { char *end = NULL; @@ -318,8 +383,16 @@ static int parse_int_strict(const char *arg, int *out) return 0; } -/* ============== Configuration Command Handlers ============== */ +/* ====== Gestionnaires de directives de configuration ====== */ +/** + * @brief Gestionnaire de la directive JsonSockLogEnabled — active ou désactive le module. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param flag 1 pour activer le module, 0 pour le désactiver. + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_enabled(cmd_parms *cmd, void *dummy, int flag) { (void)dummy; @@ -331,6 +404,14 @@ static const char *cmd_set_enabled(cmd_parms *cmd, void *dummy, int flag) return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogSocket — définit le chemin du socket Unix. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Chemin absolu vers le socket de domaine Unix. + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_socket(cmd_parms *cmd, void *dummy, const char *arg) { (void)dummy; @@ -348,6 +429,17 @@ static const char *cmd_set_socket(cmd_parms *cmd, void *dummy, const char *arg) return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogHeaders — ajoute un en-tête à journaliser. + * + * Peut être appelé plusieurs fois (directive ITERATE) pour constituer la liste + * des en-têtes HTTP à inclure dans le JSON produit. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Nom de l'en-tête HTTP à ajouter à la liste. + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_headers(cmd_parms *cmd, void *dummy, const char *arg) { (void)dummy; @@ -359,6 +451,14 @@ static const char *cmd_set_headers(cmd_parms *cmd, void *dummy, const char *arg) return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogMaxHeaders — limite le nombre d'en-têtes journalisés. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Nombre entier maximal d'en-têtes à journaliser (>= 0). + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_max_headers(cmd_parms *cmd, void *dummy, const char *arg) { int val; @@ -377,6 +477,14 @@ static const char *cmd_set_max_headers(cmd_parms *cmd, void *dummy, const char * return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogMaxHeaderValueLen — limite la longueur des valeurs d'en-têtes. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Longueur maximale en octets d'une valeur d'en-tête (>= 1). + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_max_header_value_len(cmd_parms *cmd, void *dummy, const char *arg) { int val; @@ -395,6 +503,14 @@ static const char *cmd_set_max_header_value_len(cmd_parms *cmd, void *dummy, con return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogReconnectInterval — intervalle de reconnexion au socket. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Intervalle en secondes entre deux tentatives de reconnexion (>= 0). + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_reconnect_interval(cmd_parms *cmd, void *dummy, const char *arg) { int val; @@ -413,6 +529,14 @@ static const char *cmd_set_reconnect_interval(cmd_parms *cmd, void *dummy, const return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogErrorReportInterval — fréquence des erreurs dans error_log. + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Intervalle minimal en secondes entre deux entrées d'erreur (>= 0). + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_error_report_interval(cmd_parms *cmd, void *dummy, const char *arg) { int val; @@ -431,6 +555,16 @@ static const char *cmd_set_error_report_interval(cmd_parms *cmd, void *dummy, co return NULL; } +/** + * @brief Gestionnaire de la directive JsonSockLogLevel — définit le niveau de verbosité interne. + * + * Valeurs acceptées : DEBUG, INFO, WARNING, ERROR, EMERG (insensible à la casse). + * + * @param cmd Contexte de la directive Apache. + * @param dummy Contexte de répertoire (non utilisé). + * @param arg Chaîne représentant le niveau de log souhaité. + * @return NULL en cas de succès, message d'erreur statique sinon. + */ static const char *cmd_set_log_level(cmd_parms *cmd, void *dummy, const char *arg) { (void)dummy; @@ -457,11 +591,16 @@ static const char *cmd_set_log_level(cmd_parms *cmd, void *dummy, const char *ar return NULL; } -/* ============== Socket Functions ============== */ +/* ====== Gestion du socket Unix ====== */ /** - * Check if a header name is in the sensitive headers blacklist. - * Returns 1 if header should be excluded, 0 otherwise. + * @brief Vérifie si un nom d'en-tête figure dans la liste noire des en-têtes sensibles. + * + * La comparaison est insensible à la casse. Les en-têtes sensibles (Authorization, + * Cookie, etc.) ne sont jamais journalisés pour éviter les fuites d'identifiants. + * + * @param name Nom de l'en-tête HTTP à vérifier. + * @return 1 si l'en-tête est sensible et doit être exclu, 0 sinon. */ static int is_sensitive_header(const char *name) { @@ -490,6 +629,18 @@ static int is_sensitive_header(const char *name) } \ } while(0) +/** + * @brief Tente d'établir (ou de rétablir) la connexion au socket Unix configuré. + * + * Respecte l'intervalle de reconnexion pour éviter de saturer les tentatives. + * Le socket est créé en mode non-bloquant (O_NONBLOCK). La fonction est protégée + * par le mutex interne pour la sécurité des threads. + * + * @param cfg Configuration du module (chemin du socket, intervalle de reconnexion). + * @param state État de la connexion du processus enfant courant. + * @param s Enregistrement du serveur Apache pour la journalisation des erreurs. + * @return 0 en cas de succès ou connexion déjà en cours, -1 en cas d'échec. + */ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state, server_rec *s) { apr_time_t now; @@ -569,6 +720,17 @@ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state, return 0; } +/** + * @brief Vérifie l'état de la connexion et tente de se reconnecter si nécessaire. + * + * Utilise un double-check sous verrou pour éviter les tentatives superflues + * en contexte haute concurrence. + * + * @param cfg Configuration du module. + * @param state État de la connexion du processus enfant courant. + * @param s Enregistrement du serveur Apache pour la journalisation des erreurs. + * @return 0 si le socket est prêt, -1 si la connexion n'est pas disponible. + */ static int ensure_connected(reqin_log_config_t *cfg, reqin_log_child_state_t *state, server_rec *s) { int connected; @@ -587,6 +749,20 @@ static int ensure_connected(reqin_log_config_t *cfg, reqin_log_child_state_t *st return try_connect(cfg, state, s); } +/** + * @brief Envoie un bloc de données vers le socket Unix en mode non-bloquant. + * + * En cas d'erreur de connexion (EPIPE, ECONNRESET, ENOTCONN), le socket est fermé + * et marqué pour reconnexion lors du prochain appel. L'envoi partiel est traité + * comme une erreur fatale. + * + * @param data Pointeur vers les données à envoyer. + * @param len Taille des données en octets. + * @param s Enregistrement du serveur Apache pour la journalisation des erreurs. + * @param cfg Configuration du module. + * @param state État de la connexion du processus enfant courant. + * @return 0 en cas de succès, -1 en cas d'erreur. + */ static int write_to_socket(const char *data, apr_size_t len, server_rec *s, reqin_log_config_t *cfg, reqin_log_child_state_t *state) { @@ -639,8 +815,15 @@ static int write_to_socket(const char *data, apr_size_t len, server_rec *s, return 0; } -/* ============== Request Logging Functions ============== */ +/* ====== Journalisation des requêtes ====== */ +/** + * @brief Récupère la valeur d'un en-tête HTTP entrant par son nom (insensible à la casse). + * + * @param r Enregistrement de la requête Apache. + * @param name Nom de l'en-tête à rechercher. + * @return Valeur de l'en-tête, ou NULL s'il est absent. + */ static const char *get_header(request_rec *r, const char *name) { const apr_table_t *headers = r->headers_in; @@ -656,6 +839,18 @@ static const char *get_header(request_rec *r, const char *name) return NULL; } +/** + * @brief Sérialise une requête HTTP entrante en JSON et l'envoie vers le socket Unix. + * + * Construit un objet JSON contenant les métadonnées de la requête (adresses, méthode, + * chemin, version HTTP, en-têtes demandés), en respectant les limites de taille et + * en filtrant les en-têtes sensibles. La ligne JSON est terminée par un saut de ligne. + * + * @param r Enregistrement de la requête Apache. + * @param cfg Configuration du module (liste d'en-têtes, limites, chemin socket). + * @param state État de la connexion du processus enfant courant. + * @param srv_conf Configuration complète du serveur (inclut le niveau de log). + */ static void log_request(request_rec *r, reqin_log_config_t *cfg, reqin_log_child_state_t *state, reqin_log_server_conf_t *srv_conf) { apr_pool_t *pool; @@ -917,8 +1112,17 @@ static void log_request(request_rec *r, reqin_log_config_t *cfg, reqin_log_child write_to_socket(buf.data, buf.len, s, cfg, state); } -/* ============== Apache Hooks ============== */ +/* ====== Hooks Apache ====== */ +/** + * @brief Hook post_read_request — journalise la requête entrante dès sa réception. + * + * Les sous-requêtes et les redirections internes sont ignorées afin de ne + * journaliser que la requête originale du client. + * + * @param r Enregistrement de la requête Apache. + * @return DECLINED dans tous les cas (traitement non exclusif). + */ static int reqin_log_post_read_request(request_rec *r) { reqin_log_server_conf_t *srv_conf = get_server_conf(r->server); @@ -937,6 +1141,16 @@ static int reqin_log_post_read_request(request_rec *r) return DECLINED; } +/** + * @brief Hook child_init — initialise l'état du processus enfant et établit la connexion socket. + * + * Réinitialise le descripteur de socket, crée le mutex de protection du FD et + * tente une première connexion au socket Unix. En MPM threadé, l'absence de mutex + * entraîne la désactivation du module pour garantir la sécurité des threads. + * + * @param p Pool APR du processus enfant. + * @param s Enregistrement du serveur Apache. + */ static void reqin_log_child_init(apr_pool_t *p, server_rec *s) { reqin_log_server_conf_t *srv_conf = get_server_conf(s); @@ -987,6 +1201,19 @@ static void reqin_log_child_init(apr_pool_t *p, server_rec *s) try_connect(srv_conf->config, &srv_conf->child_state, s); } +/** + * @brief Hook post_config — valide la configuration de tous les serveurs virtuels. + * + * Vérifie que chaque serveur ayant le module activé dispose d'un chemin de socket + * valide et non vide. Retourne une erreur HTTP en cas de configuration invalide, + * ce qui interrompt le démarrage d'Apache. + * + * @param pconf Pool de configuration Apache (non utilisé). + * @param plog Pool de journalisation Apache (non utilisé). + * @param ptemp Pool temporaire (non utilisé). + * @param s Premier enregistrement de serveur de la chaîne. + * @return OK en cas de succès, HTTP_INTERNAL_SERVER_ERROR sinon. + */ static int reqin_log_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { server_rec *cur; @@ -1024,6 +1251,14 @@ static int reqin_log_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t return OK; } +/** + * @brief Enregistre tous les hooks Apache utilisés par le module. + * + * Enregistre les hooks post_config, post_read_request et child_init + * avec une priorité APR_HOOK_MIDDLE. + * + * @param p Pool APR (non utilisé). + */ static void reqin_log_register_hooks(apr_pool_t *p) { (void)p;