Add IPv6 support

This commit is contained in:
Dominion0815 2018-05-21 10:26:01 +02:00 committed by GitHub
parent 87fc67f62d
commit d1c25264f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,42 +1,49 @@
/* /*
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
* mod_log_ipmask - An Apache http server modul extending mod_log_config * 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, * Copyright (C) 2008 Mario Osswald,
* Referatsleiter "Technik, Informatik, Medien" * Referatsleiter "Technik, Informatik, Medien"
* beim * beim
* chsischen Datenschutzbeauftragten * Saechsischen Datenschutzbeauftragten
* *
* Author Florian van Koten * Author Florian van Koten
* systematics NETWORK SERVICES GmbH * 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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; If not, see <http://www.gnu.org/licenses/>. * along with this program; If not, see <http://www.gnu.org/licenses/>.
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include "httpd.h" #include "httpd.h"
#include "http_config.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" #include "mod_log_config.h"
#ifndef DEFAULT_FILTER_MASK #define DEFAULT_FILTER_BITS_V4 16
#define DEFAULT_FILTER_MASK "255.255.255.0" #define DEFAULT_FILTER_BITS_V6 32
#endif
static const char *V4_PFX = "\0\0\0\0\0\0\0\0\0\0\377\377";
struct apr_ipsubnet_t { struct apr_ipsubnet_t {
int family; int family;
@ -55,175 +62,175 @@ struct apr_ipsubnet_t {
*/ */
module AP_MODULE_DECLARE_DATA log_ipmask_module; module AP_MODULE_DECLARE_DATA log_ipmask_module;
static const int BUFLEN = 48;
/** static char* to_string(struct sockaddr *sa, size_t salen, apr_pool_t* pPool) {
* @see inet_ntop4 char *buf = apr_pcalloc(pPool, BUFLEN);
*/ int rv;
static const char* ipmask_inet_ntop4(const unsigned char *src, char *dst) if (!buf) { return NULL; }
{ rv = getnameinfo(sa, salen, buf, BUFLEN, NULL, 0, NI_NUMERICHOST);
int n = 0; if (rv) { *buf = 0; }
char *next = dst; return buf;
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;
} }
/** /**
* @brief Maskiert eine IP-Adresse mit der angegebenen Filter-Maske. * @brief Maskiert eine IP-Adresse mit der angegebenen Filter-Maske.
* Die Filter-Maske kann entweder eine IP-Adresse * Die Filter-Maske entspricht
* (z.B. 255.255.255.0) * der Anzahl der zu erhaltenen Bits (z.B. 24)
* oder die Anzahl der zu erhaltenen Bits sein * Die Maske kann durch einen '/' getrennt verschiedene
* (z.B. 24) * Werte fuer IPv4 und IPv6 enthalten.
* Die Filtermaske wird in der Logger-Konfiguration angegeben; * Die Filtermaske wird in der Logger-Konfiguration angegeben;
* Beispiel %{24}h oder %{255.255.0.0}a * Beispiel %{24}h oder %{24/56}a
* @param char* pszAddress (IP-Adresse) * @param char* pszAddress (IP-Adresse)
* @param char* pszFilterMask (Filter-Maske) * @param char* pszFilterMask (Filter-Maske)
* @param apr_pool_t* pPool * @param apr_pool_t* pPool
*/ */
static const char* get_filtered_ip(char* pszAddress, char* pszFilterMask, apr_pool_t* pPool) { static const char* get_filtered_ip(char* pszAddress, char* pszFilterMask, apr_pool_t* pPool) {
char* pszFilteredIP = NULL; char* pszFilteredIP = NULL;
apr_status_t rv; apr_status_t rv;
apr_ipsubnet_t* pIPSubNet; int bitsv4, bitsv6;
struct addrinfo hints = {0}, *res = NULL;
if (*pszFilterMask == '\0') { /* parse IP-Adress */
pszFilterMask = DEFAULT_FILTER_MASK; 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 */ if (!rv && res) {
rv = apr_ipsubnet_create(&pIPSubNet, pszAddress, pszFilterMask, pPool); /* 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)) { return pszFilteredIP ? pszFilteredIP : pszAddress;
/* 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;
}; };
/** /**
* @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurück, wenn * @brief Diese Funktion gibt die IP-Adresse des Clients maskiert zurueck, wenn
* der Hostname nicht aufgelöst wurde * der Hostname nicht aufgeloest wurde
* *
* @param request_rec* pRequest (request-Struktur) * @param request_rec* pRequest (request-Struktur)
* @param char* pszMask (Konfigurationsparameter für %h aus httpd.conf) * @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( pszHost = ap_escape_logitem(
pRequest->pool, pRequest->pool,
ap_get_remote_host( ap_get_remote_host(
pRequest->connection, pRequest->connection,
pRequest->per_dir_config, pRequest->per_dir_config,
REMOTE_NAME, REMOTE_NAME,
NULL 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 request_rec* pRequest (request-Struktur)
* @param char* pszMask (Konfigurationsparameter r %a aus httpd.conf) * @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")) { if (!strcmp(pszMask, "c")) {
// Apache 2.4: %{c}a ist die IP-Adresse der Connection, mglw. ein Proxy // Apache 2.4: %{c}a ist die IP-Adresse der Connection, mglw. ein Proxy
return pRequest->connection->client_ip; 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, * @brief Diese Funktion ersetzt die LogFormat-Direktiven aus mod_log_config.c,
* die Client IP-Adressen enthalten nnen, mit eigenen Handlern * die Client IP-Adressen enthalten koennen, mit eigenen Handlern
* *
* @param apr_pool_t* p * @param apr_pool_t* p
* @param apr_pool_t* plog * @param apr_pool_t* plog
* @param apr_pool_t* ptemp * @param apr_pool_t* ptemp
*/ */
static int ipmask_pre_config(apr_pool_t* p, apr_pool_t* plog, 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; static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *ipmask_pfn_register;
ipmask_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); ipmask_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
if (ipmask_pfn_register) { if (ipmask_pfn_register) {
ipmask_pfn_register(p, "h", log_remote_host_masked, 0); ipmask_pfn_register(p, "h", log_remote_host_masked, 0);
ipmask_pfn_register(p, "a", log_remote_address_masked, 0); ipmask_pfn_register(p, "a", log_remote_address_masked, 0);
} }
return OK; return OK;
} }
/** /**
* @brief Diese Callback-Funktion registriert die pre-config-Funktion, * @brief Diese Callback-Funktion registriert die pre-config-Funktion,
* durch die die Handler r die LogFormat-Direktiven ersetzt * durch die die Handler fuer die LogFormat-Direktiven ersetzt
* werden (%a und %h). * werden (%a und %h).
* Diese pre-config-Funktion muss nach der aus mod_log_config.c * Diese pre-config-Funktion muss nach der aus mod_log_config.c
* aufgerufen werden. * aufgerufen werden.
* *
* @param apr_pool_t* p * @param apr_pool_t* p
*/ */
static void ipmask_register_hooks (apr_pool_t* p) static void ipmask_register_hooks (apr_pool_t* p)
{ {
static const char* const aszPre[] = {"mod_log_config.c", NULL}; static const char* const aszPre[] = {"mod_log_config.c", NULL};
ap_hook_pre_config(ipmask_pre_config, aszPre, NULL, APR_HOOK_FIRST); 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 * 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. * einzige Verbindung zwischen dem http-Kern und diesem Modul ist.
*/ */
module AP_MODULE_DECLARE_DATA log_ipmask_module = module AP_MODULE_DECLARE_DATA log_ipmask_module =
{ {
STANDARD20_MODULE_STUFF, /* standard stuff */ STANDARD20_MODULE_STUFF, /* standard stuff */
NULL, /* per-directory configuration structures */ NULL, /* per-directory configuration structures */
NULL, /* merge per-directory */ NULL, /* merge per-directory */
NULL, /* per-server configuration structures */ NULL, /* per-server configuration structures */
NULL, /* merge per-server */ NULL, /* merge per-server */
NULL, /* configuration directive handlers */ NULL, /* configuration directive handlers */
ipmask_register_hooks, /* Callback, um Hooks zu registrieren */ ipmask_register_hooks, /* Callback, um Hooks zu registrieren */
}; };