From d1c25264f46c869e489a50d92db06c5a466e5b72 Mon Sep 17 00:00:00 2001 From: Dominion0815 <33466236+Dominion0815@users.noreply.github.com> Date: Mon, 21 May 2018 10:26:01 +0200 Subject: [PATCH] Add IPv6 support --- mod_log_ipmask.c | 279 ++++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 136 deletions(-) diff --git a/mod_log_ipmask.c b/mod_log_ipmask.c index 25d2702..aa2e8ae 100755 --- a/mod_log_ipmask.c +++ b/mod_log_ipmask.c @@ -1,42 +1,49 @@ /* * ----------------------------------------------------------------------------- * mod_log_ipmask - An Apache http server modul extending mod_log_config - * to masquerade Client IP-Addresses in logfiles + * to masquerade Client IP-Addresses in logfiles * - * Copyright (C) 2008 Mario Oßwald, - * Referatsleiter "Technik, Informatik, Medien" - * beim - * Sächsischen Datenschutzbeauftragten + * Copyright (C) 2008 Mario Osswald, + * Referatsleiter "Technik, Informatik, Medien" + * beim + * Saechsischen Datenschutzbeauftragten * - * Author Florian van Koten - * systematics NETWORK SERVICES GmbH + * Author Florian van Koten + * systematics NETWORK SERVICES GmbH + * + * ...with modifications (C) 2012 Peter Conrad + * ...Peters IPv6 patch ported to work with apache2.4 (C) 2017 Ronny Seffner * ----------------------------------------------------------------------------- - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * ----------------------------------------------------------------------------- */ +#include +#include +#include +#include #include "httpd.h" #include "http_config.h" -#include "http_core.h" /* Für REMOTE_NAME */ +#include "http_core.h" /* Fuer REMOTE_NAME */ #include "mod_log_config.h" -#ifndef DEFAULT_FILTER_MASK -#define DEFAULT_FILTER_MASK "255.255.255.0" -#endif +#define DEFAULT_FILTER_BITS_V4 16 +#define DEFAULT_FILTER_BITS_V6 32 +static const char *V4_PFX = "\0\0\0\0\0\0\0\0\0\0\377\377"; struct apr_ipsubnet_t { int family; @@ -55,175 +62,175 @@ struct apr_ipsubnet_t { */ module AP_MODULE_DECLARE_DATA log_ipmask_module; +static const int BUFLEN = 48; -/** -* @see inet_ntop4 -*/ -static const char* ipmask_inet_ntop4(const unsigned char *src, char *dst) -{ - int n = 0; - char *next = dst; - - do { - unsigned char u = *src++; - if (u > 99) { - *next++ = '0' + u/100; - u %= 100; - *next++ = '0' + u/10; - u %= 10; - } - else if (u > 9) { - *next++ = '0' + u/10; - u %= 10; - } - *next++ = '0' + u; - *next++ = '.'; - n++; - } while (n < 4); - *--next = 0; - return dst; +static char* to_string(struct sockaddr *sa, size_t salen, apr_pool_t* pPool) { + char *buf = apr_pcalloc(pPool, BUFLEN); + int rv; + if (!buf) { return NULL; } + rv = getnameinfo(sa, salen, buf, BUFLEN, NULL, 0, NI_NUMERICHOST); + if (rv) { *buf = 0; } + return buf; } - /** -* @brief Maskiert eine IP-Adresse mit der angegebenen Filter-Maske. -* Die Filter-Maske kann entweder eine IP-Adresse -* (z.B. 255.255.255.0) -* oder die Anzahl der zu erhaltenen Bits sein -* (z.B. 24) -* Die Filtermaske wird in der Logger-Konfiguration angegeben; -* Beispiel %{24}h oder %{255.255.0.0}a -* @param char* pszAddress (IP-Adresse) -* @param char* pszFilterMask (Filter-Maske) -* @param apr_pool_t* pPool +* @brief Maskiert eine IP-Adresse mit der angegebenen Filter-Maske. +* Die Filter-Maske entspricht +* der Anzahl der zu erhaltenen Bits (z.B. 24) +* Die Maske kann durch einen '/' getrennt verschiedene +* Werte fuer IPv4 und IPv6 enthalten. +* Die Filtermaske wird in der Logger-Konfiguration angegeben; +* Beispiel %{24}h oder %{24/56}a +* @param char* pszAddress (IP-Adresse) +* @param char* pszFilterMask (Filter-Maske) +* @param apr_pool_t* pPool */ static const char* get_filtered_ip(char* pszAddress, char* pszFilterMask, apr_pool_t* pPool) { - char* pszFilteredIP = NULL; - apr_status_t rv; - apr_ipsubnet_t* pIPSubNet; + char* pszFilteredIP = NULL; + apr_status_t rv; + int bitsv4, bitsv6; + struct addrinfo hints = {0}, *res = NULL; - if (*pszFilterMask == '\0') { - pszFilterMask = DEFAULT_FILTER_MASK; - } + /* parse IP-Adress */ + hints.ai_flags = AI_NUMERICHOST | AI_V4MAPPED; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + rv = getaddrinfo(pszAddress, NULL, &hints, &res); - /* Client IP-Adresse maskieren */ - rv = apr_ipsubnet_create(&pIPSubNet, pszAddress, pszFilterMask, pPool); + if (!rv && res) { + /* ok */ + if (*pszFilterMask == '\0') { + bitsv4 = DEFAULT_FILTER_BITS_V4; + bitsv6 = DEFAULT_FILTER_BITS_V6; + } else { + char *slash = strchr(pszFilterMask, '/'); + bitsv4 = atoi(pszFilterMask); + bitsv6 = slash ? atoi(slash + 1) : bitsv4; + } + if (bitsv4 > DEFAULT_FILTER_BITS_V4) { bitsv4 = DEFAULT_FILTER_BITS_V4; } + if (bitsv6 > DEFAULT_FILTER_BITS_V6) { bitsv6 = DEFAULT_FILTER_BITS_V6; } + if (res->ai_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) res->ai_addr; + sin->sin_addr.s_addr &= htonl(0xffffffffU << (32-bitsv4)); + pszFilteredIP = to_string((struct sockaddr *) sin, res->ai_addrlen, pPool); + } else if (res->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr; + struct in6_addr *addr = &sin6->sin6_addr; + int i; + if (addr->s6_addr[0] == 0x20 && addr->s6_addr[1] == 0x02) { + bitsv6 = 16 + bitsv4; + } else if (!memcmp(addr->s6_addr, V4_PFX, 12)) { + bitsv6 = 96 + bitsv4; + } + for (i = 15; 8*i >= bitsv6; i--) { + addr->s6_addr[i] = 0; + } + if (bitsv6 & 7) { + addr->s6_addr[bitsv6 >> 3] &= 0xff << (8 - (bitsv6 & 7)); + } + pszFilteredIP = to_string((struct sockaddr *) sin6, res->ai_addrlen, pPool); + } + } + if (res) { freeaddrinfo(res); } - if (APR_STATUS_IS_EINVAL(rv)) { - /* keine IP-Adresse identifiziert (Hostname?) */ - pszFilteredIP = pszAddress; - - } else if (rv != APR_SUCCESS) { - /* Fehler beim Maskieren */ - pszFilteredIP = pszAddress; - - } else if (pIPSubNet->family != AF_INET) { - /* keine IPv4-Adresse */ - pszFilteredIP = pszFilteredIP; - - } else { - /* ok */ - pszFilteredIP = apr_pcalloc(pPool, sizeof("255.255.255.0")); - ipmask_inet_ntop4((char*)pIPSubNet->sub, pszFilteredIP); - } - - return pszFilteredIP; + return pszFilteredIP ? pszFilteredIP : pszAddress; }; /** - * @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurück, wenn - * der Hostname nicht aufgelöst wurde + * @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurueck, wenn + * der Hostname nicht aufgeloest wurde * - * @param request_rec* pRequest (request-Struktur) - * @param char* pszMask (Konfigurationsparameter für %h aus httpd.conf) + * @param request_rec* pRequest (request-Struktur) + * @param char* pszMask (Konfigurationsparameter fuer %h aus httpd.conf) */ -static const char *log_remote_host_masked(request_rec* pRequest, char* pszMask) +static const char *log_remote_host_masked(request_rec* pRequest, char* pszMask) { - char* pszHost; + char* pszHost; - pszHost = ap_escape_logitem( - pRequest->pool, - ap_get_remote_host( - pRequest->connection, + pszHost = ap_escape_logitem( + pRequest->pool, + ap_get_remote_host( + pRequest->connection, pRequest->per_dir_config, - REMOTE_NAME, - NULL - )); + REMOTE_NAME, + NULL + )); - return get_filtered_ip(pszHost, pszMask, pRequest->pool); + return get_filtered_ip(pszHost, pszMask, pRequest->pool); } /** - * @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurück + * @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurueck * - * @param request_rec* pRequest (request-Struktur) - * @param char* pszMask (Konfigurationsparameter für %a aus httpd.conf) + * @param request_rec* pRequest (request-Struktur) + * @param char* pszMask (Konfigurationsparameter fuer %a aus httpd.conf) */ -static const char *log_remote_address_masked(request_rec* pRequest, char* pszMask) +static const char *log_remote_address_masked(request_rec* pRequest, char* pszMask) { - char* pszAddress; + char* pszAddress; - if (!strcmp(pszMask, "c")) { - // Apache 2.4: %{c}a ist die IP-Adresse der Connection, mglw. ein Proxy - return pRequest->connection->client_ip; - } + if (!strcmp(pszMask, "c")) { + // Apache 2.4: %{c}a ist die IP-Adresse der Connection, mglw. ein Proxy + return pRequest->connection->client_ip; + } - pszAddress = pRequest->useragent_ip; + pszAddress = pRequest->useragent_ip; - return get_filtered_ip(pszAddress, pszMask, pRequest->pool); + return get_filtered_ip(pszAddress, pszMask, pRequest->pool); } /** - * @brief Diese Funktion ersetzt die LogFormat-Direktiven aus mod_log_config.c, - * die Client IP-Adressen enthalten können, mit eigenen Handlern - * - * @param apr_pool_t* p - * @param apr_pool_t* plog - * @param apr_pool_t* ptemp + * @brief Diese Funktion ersetzt die LogFormat-Direktiven aus mod_log_config.c, + * die Client IP-Adressen enthalten koennen, mit eigenen Handlern + * + * @param apr_pool_t* p + * @param apr_pool_t* plog + * @param apr_pool_t* ptemp */ static int ipmask_pre_config(apr_pool_t* p, apr_pool_t* plog, apr_pool_t* ptemp) { - static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *ipmask_pfn_register; - - ipmask_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); - if (ipmask_pfn_register) { - ipmask_pfn_register(p, "h", log_remote_host_masked, 0); - ipmask_pfn_register(p, "a", log_remote_address_masked, 0); - } - - return OK; + static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *ipmask_pfn_register; + + ipmask_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); + if (ipmask_pfn_register) { + ipmask_pfn_register(p, "h", log_remote_host_masked, 0); + ipmask_pfn_register(p, "a", log_remote_address_masked, 0); + } + + return OK; } /** - * @brief Diese Callback-Funktion registriert die pre-config-Funktion, - * durch die die Handler für die LogFormat-Direktiven ersetzt - * werden (%a und %h). - * Diese pre-config-Funktion muss nach der aus mod_log_config.c - * aufgerufen werden. - * - * @param apr_pool_t* p + * @brief Diese Callback-Funktion registriert die pre-config-Funktion, + * durch die die Handler fuer die LogFormat-Direktiven ersetzt + * werden (%a und %h). + * Diese pre-config-Funktion muss nach der aus mod_log_config.c + * aufgerufen werden. + * + * @param apr_pool_t* p */ static void ipmask_register_hooks (apr_pool_t* p) { - static const char* const aszPre[] = {"mod_log_config.c", NULL}; - ap_hook_pre_config(ipmask_pre_config, aszPre, NULL, APR_HOOK_FIRST); + static const char* const aszPre[] = {"mod_log_config.c", NULL}; + ap_hook_pre_config(ipmask_pre_config, aszPre, NULL, APR_HOOK_FIRST); } /* - * Deklaration und Veröffentlichung der Modul-Datenstruktur. + * Deklaration und Veroeffentlichung der Modul-Datenstruktur. * Der Name dieser Struktur ist wichtig ('log_ipmask_module') - er muss - * mit dem Namen des Moduls übereinstimmen, da diese Struktur die + * mit dem Namen des Moduls uebereinstimmen, da diese Struktur die * einzige Verbindung zwischen dem http-Kern und diesem Modul ist. */ module AP_MODULE_DECLARE_DATA log_ipmask_module = { - STANDARD20_MODULE_STUFF, /* standard stuff */ - NULL, /* per-directory configuration structures */ - NULL, /* merge per-directory */ - NULL, /* per-server configuration structures */ - NULL, /* merge per-server */ - NULL, /* configuration directive handlers */ - ipmask_register_hooks, /* Callback, um Hooks zu registrieren */ + STANDARD20_MODULE_STUFF, /* standard stuff */ + NULL, /* per-directory configuration structures */ + NULL, /* merge per-directory */ + NULL, /* per-server configuration structures */ + NULL, /* merge per-server */ + NULL, /* configuration directive handlers */ + ipmask_register_hooks, /* Callback, um Hooks zu registrieren */ };