Add /api/logs/{ftl,http,ph7}

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2023-01-18 04:22:50 +01:00
parent 2e2253c77c
commit 1ae49c7baf
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
22 changed files with 299 additions and 220 deletions

View File

@ -121,8 +121,6 @@ set(sources
dhcp-discover.h
dnsmasq_interface.c
dnsmasq_interface.h
fifo.c
fifo.h
edns0.c
edns0.h
enums.h

View File

@ -27,51 +27,54 @@ static struct {
const char *uri;
const char *parameters;
int (*func)(struct ftl_conn *api);
const bool opts[2];
struct api_options opts;
bool require_auth;
enum http_method methods;
} api_request[] = {
// URI ARGUMENTS FUNCTION OPTIONS AUTH ALLOWED METHODS
// Note: The order of appearance matters here, more specific URIs have to
// appear *before* less specific URIs: 1. "/a/b/c", 2. "/a/b", 3. "/a"
{ "/api/dns/blocking", "", api_dns_blocking, { false, false }, true, HTTP_GET | HTTP_POST },
{ "/api/dns/cache", "", api_dns_cache, { false, false }, true, HTTP_GET },
{ "/api/dns/port", "", api_dns_port, { false, false }, true, HTTP_GET },
{ "/api/dns/entries", "", api_dns_entries, { false, false }, true, HTTP_GET },
{ "/api/dns/entries", "/{ip}/{host}", api_dns_entries, { false, false }, true, HTTP_PUT | HTTP_DELETE },
{ "/api/clients", "/{client}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/domains", "/{type}/{kind}/{domain}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/groups", "/{name}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/lists", "/{list}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/ftl/client", "", api_ftl_client, { false, false }, false, HTTP_GET },
{ "/api/ftl/sysinfo", "", api_ftl_sysinfo, { false, false }, true, HTTP_GET },
{ "/api/ftl/dbinfo", "", api_ftl_dbinfo, { false, false }, true, HTTP_GET },
{ "/api/logs/dnsmasq", "", api_logs_dnsmasq, { false, false }, true, HTTP_GET },
{ "/api/history/clients", "", api_history_clients, { false, false }, true, HTTP_GET },
{ "/api/history/database/clients", "", api_history_database_clients, { false, false }, true, HTTP_GET },
{ "/api/history/database", "", api_history_database, { false, false }, true, HTTP_GET },
{ "/api/history", "", api_history, { false, false }, true, HTTP_GET },
{ "/api/queries/suggestions", "", api_queries_suggestions, { false, false }, true, HTTP_GET },
{ "/api/queries", "", api_queries, { false, false }, true, HTTP_GET },
{ "/api/stats/summary", "", api_stats_summary, { false, false }, true, HTTP_GET },
{ "/api/stats/query_types", "", api_stats_query_types, { false, false }, true, HTTP_GET },
{ "/api/stats/upstreams", "", api_stats_upstreams, { false, false }, true, HTTP_GET },
{ "/api/stats/top_domains", "", api_stats_top_domains, { false, false }, true, HTTP_GET },
{ "/api/stats/top_clients", "", api_stats_top_clients, { false, false }, true, HTTP_GET },
{ "/api/stats/recent_blocked", "", api_stats_recentblocked, { false, false }, true, HTTP_GET },
{ "/api/stats/database/top_domains", "", api_stats_database_top_items, { false, true }, true, HTTP_GET },
{ "/api/stats/database/top_clients", "", api_stats_database_top_items, { false, false }, true, HTTP_GET },
{ "/api/stats/database/summary", "", api_stats_database_summary, { false, false }, true, HTTP_GET },
{ "/api/stats/database/query_types", "", api_stats_database_query_types, { false, false }, true, HTTP_GET },
{ "/api/stats/database/upstreams", "", api_stats_database_upstreams, { false, false }, true, HTTP_GET },
{ "/api/version", "", api_version, { false, false }, true, HTTP_GET },
{ "/api/auth", "", api_auth, { false, false }, false, HTTP_GET | HTTP_POST | HTTP_DELETE },
{ "/api/config", "", api_config, { false, false }, true, HTTP_GET | HTTP_PATCH },
{ "/api/network/gateway", "", api_network_gateway, { false, false }, true, HTTP_GET },
{ "/api/network/interfaces", "", api_network_interfaces, { false, false }, true, HTTP_GET },
{ "/api/network/devices", "", api_network_devices, { false, false }, true, HTTP_GET },
{ "/api/endpoints", "", api_endpoints, { false, false }, true, HTTP_GET },
{ "/api/docs", "", api_docs, { false, false }, false, HTTP_GET },
{ "/api/dns/blocking", "", api_dns_blocking, { false, 0 }, true, HTTP_GET | HTTP_POST },
{ "/api/dns/cache", "", api_dns_cache, { false, 0 }, true, HTTP_GET },
{ "/api/dns/port", "", api_dns_port, { false, 0 }, true, HTTP_GET },
{ "/api/dns/entries", "", api_dns_entries, { false, 0 }, true, HTTP_GET },
{ "/api/dns/entries", "/{ip}/{host}", api_dns_entries, { false, 0 }, true, HTTP_PUT | HTTP_DELETE },
{ "/api/clients", "/{client}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/domains", "/{type}/{kind}/{domain}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/groups", "/{name}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/lists", "/{list}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE },
{ "/api/ftl/client", "", api_ftl_client, { false, 0 }, false, HTTP_GET },
{ "/api/ftl/sysinfo", "", api_ftl_sysinfo, { false, 0 }, true, HTTP_GET },
{ "/api/ftl/dbinfo", "", api_ftl_dbinfo, { false, 0 }, true, HTTP_GET },
{ "/api/logs/dnsmasq", "", api_logs, { false, FIFO_DNSMASQ }, true, HTTP_GET },
{ "/api/logs/ftl", "", api_logs, { false, FIFO_FTL }, true, HTTP_GET },
{ "/api/logs/http", "", api_logs, { false, FIFO_CIVETWEB }, true, HTTP_GET },
{ "/api/logs/ph7", "", api_logs, { false, FIFO_PH7 }, true, HTTP_GET },
{ "/api/history/clients", "", api_history_clients, { false, 0 }, true, HTTP_GET },
{ "/api/history/database/clients", "", api_history_database_clients, { false, 0 }, true, HTTP_GET },
{ "/api/history/database", "", api_history_database, { false, 0 }, true, HTTP_GET },
{ "/api/history", "", api_history, { false, 0 }, true, HTTP_GET },
{ "/api/queries/suggestions", "", api_queries_suggestions, { false, 0 }, true, HTTP_GET },
{ "/api/queries", "", api_queries, { false, 0 }, true, HTTP_GET },
{ "/api/stats/summary", "", api_stats_summary, { false, 0 }, true, HTTP_GET },
{ "/api/stats/query_types", "", api_stats_query_types, { false, 0 }, true, HTTP_GET },
{ "/api/stats/upstreams", "", api_stats_upstreams, { false, 0 }, true, HTTP_GET },
{ "/api/stats/top_domains", "", api_stats_top_domains, { false, 0 }, true, HTTP_GET },
{ "/api/stats/top_clients", "", api_stats_top_clients, { false, 0 }, true, HTTP_GET },
{ "/api/stats/recent_blocked", "", api_stats_recentblocked, { false, 0 }, true, HTTP_GET },
{ "/api/stats/database/top_domains", "", api_stats_database_top_items, { true, 0 }, true, HTTP_GET },
{ "/api/stats/database/top_clients", "", api_stats_database_top_items, { false, 0 }, true, HTTP_GET },
{ "/api/stats/database/summary", "", api_stats_database_summary, { false, 0 }, true, HTTP_GET },
{ "/api/stats/database/query_types", "", api_stats_database_query_types, { false, 0 }, true, HTTP_GET },
{ "/api/stats/database/upstreams", "", api_stats_database_upstreams, { false, 0 }, true, HTTP_GET },
{ "/api/version", "", api_version, { false, 0 }, true, HTTP_GET },
{ "/api/auth", "", api_auth, { false, 0 }, false, HTTP_GET | HTTP_POST | HTTP_DELETE },
{ "/api/config", "", api_config, { false, 0 }, true, HTTP_GET | HTTP_PATCH },
{ "/api/network/gateway", "", api_network_gateway, { false, 0 }, true, HTTP_GET },
{ "/api/network/interfaces", "", api_network_interfaces, { false, 0 }, true, HTTP_GET },
{ "/api/network/devices", "", api_network_devices, { false, 0 }, true, HTTP_GET },
{ "/api/endpoints", "", api_endpoints, { false, 0 }, true, HTTP_GET },
{ "/api/docs", "", api_docs, { false, 0 }, false, HTTP_GET },
};
int api_handler(struct mg_connection *conn, void *ignored)
@ -85,7 +88,7 @@ int api_handler(struct mg_connection *conn, void *ignored)
NULL,
{ false, NULL, NULL, 0u },
{ false },
{ false, false }
{ false, 0 }
};
// Allocate memory for the payload
@ -119,7 +122,7 @@ int api_handler(struct mg_connection *conn, void *ignored)
if((api.item = startsWith(api_request[i].uri, &api)) != NULL)
{
// Copy options to API struct
memcpy(api.opts, api_request[i].opts, sizeof(api.opts));
memcpy(&api.opts, &api_request[i].opts, sizeof(api.opts));
// Verify requesting client is allowed to see this ressource
if(api_request[i].require_auth && check_client_auth(&api) == API_AUTH_UNAUTHORIZED)

View File

@ -54,7 +54,7 @@ int get_system_obj(struct ftl_conn *api, cJSON *system);
int api_config(struct ftl_conn *api);
// Log methods
int api_logs_dnsmasq(struct ftl_conn *api);
int api_logs(struct ftl_conn *api);
// Network methods
int api_network_gateway(struct ftl_conn *api);

View File

@ -12,30 +12,10 @@
#include "webserver/http-common.h"
#include "webserver/json_macros.h"
#include "api/api.h"
// struct fifologData
#include "fifo.h"
// sysinfo()
#include <sys/sysinfo.h>
// get_blockingstatus()
#include "setupVars.h"
// counters
#include "shmem.h"
// get_FTL_db_filesize()
#include "files.h"
// get_sqlite3_version()
#include "database/common.h"
// get_number_of_queries_in_DB()
#include "database/query-table.h"
// getgrgid()
#include <grp.h>
// config struct
#include "config/config.h"
// struct clientsData
#include "datastructure.h"
// Routing information and flags
#include <net/route.h>
// Interate through directories
#include <dirent.h>
// INT_MIN, INT_MAX, ...
#include <limits.h>
// writeFTLtoml()

View File

@ -11,6 +11,99 @@ components:
description: |
This API hook returns content from the log of the embedded DNS resolver `dnsmasq`.
Every successful request will return a `nextID`.
This ID can be used on the next request to only get lines which were added *after* the last request.
This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary.
The expected behavior for an immediate re-request of a log line with the same ID is an empty response.
As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one.
parameters:
- $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log'
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
ftl:
get:
summary: Get DNS log content
tags:
- "FTL information"
operationId: "get_dns_log"
description: |
This API hook returns content from the log of FTL.
Every successful request will return a `nextID`.
This ID can be used on the next request to only get lines which were added *after* the last request.
This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary.
The expected behavior for an immediate re-request of a log line with the same ID is an empty response.
As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one.
parameters:
- $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log'
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
http:
get:
summary: Get DNS log content
tags:
- "FTL information"
operationId: "get_dns_log"
description: |
This API hook returns content from the log of the embedded CivetWeb HTTP server.
Every successful request will return a `nextID`.
This ID can be used on the next request to only get lines which were added *after* the last request.
This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary.
The expected behavior for an immediate re-request of a log line with the same ID is an empty response.
As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one.
parameters:
- $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID'
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log'
- $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
ph7:
get:
summary: Get DNS log content
tags:
- "FTL information"
operationId: "get_dns_log"
description: |
This API hook returns content from the log of the embedded PH7 engine.
Every successful request will return a `nextID`.
This ID can be used on the next request to only get lines which were added *after* the last request.
This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary.

View File

@ -153,6 +153,15 @@ paths:
/logs/dnsmasq:
$ref: 'logs.yaml#/components/paths/logs/dnsmasq'
/logs/ftl:
$ref: 'logs.yaml#/components/paths/logs/ftl'
/logs/http:
$ref: 'logs.yaml#/components/paths/logs/http'
/logs/ph7:
$ref: 'logs.yaml#/components/paths/logs/ph7'
/endpoints:
$ref: 'endpoints.yaml#/components/paths/endpoints'

View File

@ -13,11 +13,11 @@
#include "webserver/json_macros.h"
#include "api/api.h"
// struct fifologData
#include "fifo.h"
#include "log.h"
// fifologData is allocated in shared memory for cross-fork compatibility
fifologData *fifo_log = NULL;
int api_logs_dnsmasq(struct ftl_conn *api)
int api_logs(struct ftl_conn *api)
{
unsigned int start = 0u;
if(api->request->query_string != NULL)
@ -26,22 +26,22 @@ int api_logs_dnsmasq(struct ftl_conn *api)
unsigned int nextID;
if(get_uint_var(api->request->query_string, "nextID", &nextID))
{
if(nextID >= fifo_log->next_id)
if(nextID >= fifo_log->logs[api->opts.which].next_id)
{
// Do not return any data
start = LOG_SIZE;
}
else if((fifo_log->next_id > LOG_SIZE) && nextID < (fifo_log->next_id) - LOG_SIZE)
else if((fifo_log->logs[api->opts.which].next_id > LOG_SIZE) && nextID < (fifo_log->logs[api->opts.which].next_id) - LOG_SIZE)
{
// Requested an ID smaller than the lowest one we have
// We return the entire buffer
start = 0u;
}
else if(fifo_log->next_id >= LOG_SIZE)
else if(fifo_log->logs[api->opts.which].next_id >= LOG_SIZE)
{
// Reply with partial buffer, measure from the end
// (the log is full)
start = LOG_SIZE - (fifo_log->next_id - nextID);
start = LOG_SIZE - (fifo_log->logs[api->opts.which].next_id - nextID);
}
else
{
@ -57,19 +57,19 @@ int api_logs_dnsmasq(struct ftl_conn *api)
cJSON *log = JSON_NEW_ARRAY();
for(unsigned int i = start; i < LOG_SIZE; i++)
{
if(fifo_log->timestamp[i] < 1.0)
if(fifo_log->logs[api->opts.which].timestamp[i] < 1.0)
{
// Uninitialized buffer entry
break;
}
cJSON *entry = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(entry, "timestamp", fifo_log->timestamp[i]);
JSON_REF_STR_IN_OBJECT(entry, "message", fifo_log->message[i]);
JSON_ADD_NUMBER_TO_OBJECT(entry, "timestamp", fifo_log->logs[api->opts.which].timestamp[i]);
JSON_REF_STR_IN_OBJECT(entry, "message", fifo_log->logs[api->opts.which].message[i]);
JSON_ADD_ITEM_TO_ARRAY(log, entry);
}
JSON_ADD_ITEM_TO_OBJECT(json, "log", log);
JSON_ADD_NUMBER_TO_OBJECT(json, "nextID", fifo_log->next_id);
JSON_ADD_NUMBER_TO_OBJECT(json, "nextID", fifo_log->logs[api->opts.which].next_id);
// Send data
JSON_SEND_OBJECT(json);

View File

@ -116,9 +116,6 @@ int api_stats_top_domains(struct ftl_conn *api)
int temparray[counters->domains][2], count = 10;
bool audit = false;
// Get options from API struct
bool blocked = api->opts[0]; // Can be overwritten by query string
// Exit before processing any data if requested via config setting
if(config.misc.privacylevel.v.privacy_level >= PRIVACY_HIDE_DOMAINS)
{
@ -133,7 +130,8 @@ int api_stats_top_domains(struct ftl_conn *api)
JSON_SEND_OBJECT(json);
}
// /api/stats/top_domains?blocked=true is allowed as well
bool blocked = false; // Can be overwritten by query string
// /api/stats/top_domains?blocked=true
if(api->request->query_string != NULL)
{
// Should blocked clients be shown?
@ -267,9 +265,6 @@ int api_stats_top_clients(struct ftl_conn *api)
int temparray[counters->clients][2], count = 10;
bool includezeroclients = false;
// Get options from API struct
bool blocked = api->opts[0]; // Can be overwritten by query string
// Exit before processing any data if requested via config setting
if(config.misc.privacylevel.v.privacy_level >= PRIVACY_HIDE_DOMAINS_CLIENTS)
{
@ -284,7 +279,7 @@ int api_stats_top_clients(struct ftl_conn *api)
JSON_SEND_OBJECT(json);
}
// /api/stats/top_clients9?blocked=true is allowed as well
bool blocked = false; // /api/stats/top_clients?blocked=true
if(api->request->query_string != NULL)
{
// Should blocked clients be shown?

View File

@ -180,8 +180,8 @@ int api_stats_database_top_items(struct ftl_conn *api)
double from = 0.0, until = 0.0;
// Get options from API struct
bool blocked = api->opts[0]; // Can be overwritten by query string
const bool domains = api->opts[1];
bool blocked = false; // Can be overwritten by query string
const bool domains = api->opts.domains;
// Get parameters from query string
if(api->request->query_string != NULL)

View File

@ -50,7 +50,6 @@ extern void print_dnsmasq_version(const char *yellow, const char *green, const c
extern int sqlite3_shell_main(int argc, char **argv);
bool dnsmasq_debug = false;
bool no_ftl_logging = false;
bool daemonmode = true, cli_mode = false;
int argc_dnsmasq = 0;
const char** argv_dnsmasq = NULL;
@ -244,7 +243,7 @@ void parse_args(int argc, char* argv[])
const char *arg[2];
arg[0] = "";
arg[1] = "--test";
no_ftl_logging = true;
log_ctrl(false, true);
exit(main_dnsmasq(2, arg));
}
@ -259,7 +258,7 @@ void parse_args(int argc, char* argv[])
sprintf(filename, "--conf-file=%s", argv[2]);
arg[1] = filename;
arg[2] = "--test";
no_ftl_logging = true;
log_ctrl(false, true);
exit(main_dnsmasq(3, arg));
}

View File

@ -15,7 +15,6 @@ void parse_args(int argc, char* argv[]);
extern bool daemonmode, cli_mode, dnsmasq_debug;
extern int argc_dnsmasq;
extern const char ** argv_dnsmasq;
extern bool no_ftl_logging;
const char *cli_tick(void) __attribute__ ((pure));
const char *cli_cross(void) __attribute__ ((pure));

View File

@ -29,6 +29,7 @@
#include "capabilities.h"
#include "resolve.h"
#include "files.h"
// add_to_fifo_buffer() u.a.
#include "log.h"
// Prototype of getCacheInformation()
#include "api/api.h"
@ -47,8 +48,6 @@
#include "api/api_helper.h"
// logg_rate_limit_message()
#include "database/message-table.h"
// add_to_dnsmasq_log_fifo_buffer()
#include "fifo.h"
// http_init()
#include "webserver/webserver.h"
// type struct sqlite3_stmt_vec
@ -3293,10 +3292,14 @@ static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnsse
// Add dnsmasq log line to internal FIFO buffer (can be queried via the API)
void FTL_dnsmasq_log(const char *payload, const int length)
{
// e.g. on config testing
if(no_ftl_logging)
return;
add_to_dnsmasq_log_fifo_buffer(payload, length);
// Lock SHM
lock_shm();
// Add to FIFO buffer
add_to_fifo_buffer(FIFO_DNSMASQ, payload, length);
// Unlock SHM
unlock_shm();
}
// Check sizes of all important in-memory objects. This routine returns the number of

View File

@ -209,11 +209,6 @@ enum timers {
LAST_TIMER
} __attribute__ ((packed));
enum web_code {
HTTP_INFO,
PH7_ERROR
} __attribute__ ((packed));
enum refresh_hostnames {
REFRESH_ALL,
REFRESH_IPV4_ONLY,
@ -287,4 +282,12 @@ enum listening_mode {
LISTEN_BIND
} __attribute__ ((packed));
enum fifo_logs {
FIFO_FTL = 1,
FIFO_DNSMASQ,
FIFO_CIVETWEB,
FIFO_PH7,
FIFO_MAX
} __attribute__ ((packed));
#endif // ENUMS_H

View File

@ -1,51 +0,0 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* dnsmasq FIFO log for Pi-hole's API
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "fifo.h"
// {un,}lock_shm()
#include "shmem.h"
// double_time()
#include "log.h"
void add_to_dnsmasq_log_fifo_buffer(const char *payload, const size_t length)
{
// Lock SHM
lock_shm();
unsigned int idx = fifo_log->next_id++;
if(idx >= LOG_SIZE)
{
// Log is full, move everything one slot forward to make space for a new record at the end
// This pruges the oldest message from the list (it is overwritten by the second message)
memmove(&fifo_log->message[0][0], &fifo_log->message[1][0], (LOG_SIZE - 1u) * MAX_MSG_FIFO);
memmove(&fifo_log->timestamp[0], &fifo_log->timestamp[1], (LOG_SIZE - 1u) * sizeof(fifo_log->timestamp[0]));
idx = LOG_SIZE - 1u;
}
// Copy relevant string into temporary buffer
size_t copybytes = length < MAX_MSG_FIFO ? length : MAX_MSG_FIFO;
memcpy(fifo_log->message[idx], payload, copybytes);
// Zero-terminate buffer, truncate newline if found
if(fifo_log->message[idx][copybytes - 1u] == '\n')
{
fifo_log->message[idx][copybytes - 1u] = '\0';
}
else
{
fifo_log->message[idx][copybytes] = '\0';
}
// Set timestamp
fifo_log->timestamp[idx] = double_time();
// Unlock SHM
unlock_shm();
}

View File

@ -1,33 +0,0 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* dnsmasq FIFO log prototypes
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef API_FTL_H
#define API_FTL_H
#include "FTL.h"
/* From RFC 3164 */
#define MAX_MSG_FIFO 1024u
// How many messages do we keep in memory (FIFO message buffer)?
// This number multiplied by MAX_MSG_FIFO (see above) gives the total buffer size
// Defaults to 128 [use 128 KB of memory for the log]
#define LOG_SIZE 128u
void add_to_dnsmasq_log_fifo_buffer(const char *payload, const size_t length);
typedef struct {
unsigned int next_id;
double timestamp[LOG_SIZE];
char message[LOG_SIZE][MAX_MSG_FIFO];
} fifologData;
extern fifologData *fifo_log;
#endif // API_FTL_H

View File

@ -294,6 +294,13 @@ void __attribute__ ((format (gnu_printf, 3, 4))) _FTL_log(const int priority, co
// Print to log file or syslog
if(print_log)
{
// Add line to FIFO buffer
char buffer[MAX_MSG_FIFO + 1u];
va_start(args, format);
const size_t len = vsnprintf(buffer, MAX_MSG_FIFO, format, args) + 1u; /* include zero-terminator */
va_end(args);
add_to_fifo_buffer(FIFO_FTL, buffer, len > MAX_MSG_FIFO ? MAX_MSG_FIFO : len);
if(config.files.log.ftl.v.s != NULL)
{
// Open log file
@ -332,40 +339,51 @@ void __attribute__ ((format (gnu_printf, 3, 4))) _FTL_log(const int priority, co
}
}
static FILE * __attribute__((malloc, warn_unused_result)) open_web_log(const enum web_code code)
static FILE * __attribute__((malloc, warn_unused_result)) open_web_log(const enum fifo_logs which)
{
// Open the log file in append/create mode
char *file = NULL;
switch (code)
switch (which)
{
case HTTP_INFO:
file = config.files.http_info.v.s;
break;
case PH7_ERROR:
file = config.files.ph7_error.v.s;
break;
default:
file = config.files.ph7_error.v.s;
break;
case FIFO_CIVETWEB:
file = config.files.http_info.v.s;
break;
case FIFO_PH7:
file = config.files.ph7_error.v.s;
break;
case FIFO_FTL:
case FIFO_DNSMASQ:
case FIFO_MAX:
default:
log_err("Invalid logging requested");
return NULL;
}
return fopen(file, "a+");
}
void __attribute__ ((format (gnu_printf, 2, 3))) logg_web(enum web_code code, const char *format, ...)
void __attribute__ ((format (gnu_printf, 2, 3))) logg_web(enum fifo_logs which, const char *format, ...)
{
char timestring[84] = "";
const time_t now = time(NULL);
va_list args;
// Add line to FIFO buffer
char buffer[MAX_MSG_FIFO + 1u];
va_start(args, format);
const size_t len = vsnprintf(buffer, MAX_MSG_FIFO, format, args) + 1u; /* include zero-terminator */
va_end(args);
add_to_fifo_buffer(which, buffer, len > MAX_MSG_FIFO ? MAX_MSG_FIFO : len);
// Get human-readable time
get_timestr(timestring, time(NULL), true);
get_timestr(timestring, now, true);
// Get and log PID of current process to avoid ambiguities when more than one
// pihole-FTL instance is logging into the same file
const long pid = (long)getpid();
// Open web log file
FILE *weblog = open_web_log(code);
FILE *weblog = open_web_log(which);
// Write to web log file
if(weblog != NULL)
@ -481,7 +499,7 @@ void format_time(char buffer[42], unsigned long seconds, double milliseconds)
void FTL_log_dnsmasq_fatal(const char *format, ...)
{
if(no_ftl_logging)
if(!print_log)
return;
// Build a complete string from possible multi-part string passed from dnsmasq
char message[256] = { 0 };
@ -703,3 +721,39 @@ void dnsmasq_diagnosis_warning(char *message)
// Crop away any existing initial "warning: "
logg_warn_dnsmasq_message(skipStr("warning: ", message));
}
void add_to_fifo_buffer(const enum fifo_logs which, const char *payload, const size_t length)
{
const double now = double_time();
// Do not try to log when shared memory isn't initialized yet
if(!fifo_log)
return;
unsigned int idx = fifo_log->logs[which].next_id++;
if(idx >= LOG_SIZE)
{
// Log is full, move everything one slot forward to make space for a new record at the end
// This pruges the oldest message from the list (it is overwritten by the second message)
memmove(&fifo_log->logs[which].message[0][0], &fifo_log->logs[which].message[1][0], (LOG_SIZE - 1u) * MAX_MSG_FIFO);
memmove(&fifo_log->logs[which].timestamp[0], &fifo_log->logs[which].timestamp[1], (LOG_SIZE - 1u) * sizeof(fifo_log->logs[which].timestamp[0]));
idx = LOG_SIZE - 1u;
}
// Copy relevant string into temporary buffer
size_t copybytes = length < MAX_MSG_FIFO ? length : MAX_MSG_FIFO;
memcpy(fifo_log->logs[which].message[idx], payload, copybytes);
// Zero-terminate buffer, truncate newline if found
if(fifo_log->logs[which].message[idx][copybytes - 1u] == '\n')
{
fifo_log->logs[which].message[idx][copybytes - 1u] = '\0';
}
else
{
fifo_log->logs[which].message[idx][copybytes] = '\0';
}
// Set timestamp
fifo_log->logs[which].timestamp[idx] = now;
}

View File

@ -32,7 +32,7 @@ void log_FTL_version(bool crashreport);
double double_time(void);
void get_timestr(char * const timestring, const time_t timein, const bool millis);
void debugstr(const enum debug_flag flag, const char **name);
void logg_web(enum web_code code, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
void logg_web(enum fifo_logs which, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
const char *get_ordinal_suffix(unsigned int number) __attribute__ ((const));
void print_FTL_version(void);
void dnsmasq_diagnosis_warning(char *message);
@ -61,4 +61,24 @@ int blocked_queries(void) __attribute__ ((pure));
const char *short_path(const char *full_path) __attribute__ ((pure));
/* From RFC 3164 */
#define MAX_MSG_FIFO 1024u
// How many messages do we keep in memory (FIFO message buffer)?
// This number multiplied by MAX_MSG_FIFO (see above) gives the total buffer size
// Defaults to 128 [use 128 KB of memory for the log]
#define LOG_SIZE 128u
void add_to_fifo_buffer(const enum fifo_logs which, const char *payload, const size_t length);
typedef struct {
struct {
unsigned int next_id;
double timestamp[LOG_SIZE];
char message[LOG_SIZE][MAX_MSG_FIFO];
} logs[FIFO_MAX];
} fifologData;
extern fifologData *fifo_log;
#endif //LOG_H

View File

@ -126,8 +126,8 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/******************************* Pi-hole modification ******************************/
enum web_code { HTTP_INFO, PH7_ERROR };
extern void logg_web(enum web_code code, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
#include "enums.h"
extern void logg_web(enum fifo_logs which, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
/***********************************************************************************/
/* $SymiscID: ph7.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable <chm@symisc.net> $ */
#include <stdarg.h> /* needed for the definition of va_list */
@ -5010,7 +5010,7 @@ static sxi32 VmThrowException(ph7_vm *pVm,ph7_class_instance *pThis);
static sxi32 VmCallErrorHandler(ph7_vm *pVm,SyBlob *pMsg)
{
/* Invoke the output consumer callback */
logg_web(PH7_ERROR, "%.*s", SyBlobLength(pMsg), (char *)SyBlobData(pMsg));
logg_web(FIFO_PH7, "%.*s", SyBlobLength(pMsg), (char *)SyBlobData(pMsg));
return SXRET_OK;
}
/*

View File

@ -16,8 +16,6 @@
#include "config/config.h"
// data getter functions
#include "datastructure.h"
// fifologData
#include "fifo.h"
// get_num_regex()
#include "regex_r.h"
// sleepms()
@ -90,7 +88,7 @@ static SharedMemory *sharedMemories[] = { &shm_lock,
&shm_settings,
&shm_dns_cache,
&shm_per_client_regex,
&shm_fifo_log };
&shm_fifo_log };
#define NUM_SHMEM (sizeof(sharedMemories)/sizeof(SharedMemory*))
// Variable size array structs
@ -402,6 +400,7 @@ static void remap_shm(void)
void _lock_shm(const char *func, const int line, const char *file)
{
log_debug(DEBUG_LOCKS, "Waiting for SHM lock in %s() (%s:%i)", func, file, line);
log_debug(DEBUG_LOCKS, "SHM lock: %p", shmLock);
int result = pthread_mutex_lock(&shmLock->lock.outer);

View File

@ -11,8 +11,10 @@
#define HTTP_H
// External components
#include "../civetweb/civetweb.h"
#include "../cJSON/cJSON.h"
#include "civetweb/civetweb.h"
#include "cJSON/cJSON.h"
// enum fifo_logs
#include "enums.h"
// strlen()
#include <string.h>
@ -29,6 +31,12 @@ enum http_method {
HTTP_PATCH = 1 << 3,
HTTP_DELETE = 1 << 4,
};
struct api_options {
bool domains :1;
enum fifo_logs which;
};
struct ftl_conn {
struct mg_connection *conn;
const struct mg_request_info *request;
@ -45,7 +53,7 @@ struct ftl_conn {
bool restart;
} ftl;
bool opts[2];
struct api_options opts;
};

View File

@ -84,20 +84,20 @@ int ph7_handler(struct mg_connection *conn, void *cbdata)
{
if( rc == PH7_IO_ERR )
{
logg_web(PH7_ERROR, "%s: IO error while opening the target file", full_path);
logg_web(FIFO_PH7, "%s: IO error while opening the target file", full_path);
// Fall back to HTTP server to handle the 404 event
return 0;
}
else if( rc == PH7_VM_ERR )
{
logg_web(PH7_ERROR, "%s: VM initialization error", full_path);
logg_web(FIFO_PH7, "%s: VM initialization error", full_path);
// Mark file as processed - this prevents the HTTP server
// from printing the raw PHP source code to the user
return 1;
}
else
{
logg_web(PH7_ERROR, "%s: Compile error (%d)", full_path, rc);
logg_web(FIFO_PH7, "%s: Compile error (%d)", full_path, rc);
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"PHP compilation error, check %s for further details.",
@ -109,7 +109,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata)
ph7_config(pEngine, PH7_CONFIG_ERR_LOG, &zErrLog, &niLen);
if( niLen > 0 ){
/* zErrLog is null terminated */
logg_web(PH7_ERROR, " ---> %s", zErrLog);
logg_web(FIFO_PH7, " ---> %s", zErrLog);
}
// Mark file as processed - this prevents the HTTP server
// from printing the raw PHP source code to the user
@ -134,7 +134,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata)
{
rc = ph7_create_function(pVm, aFunc[i].zName, aFunc[i].xProc, NULL /* NULL: No private data */);
if( rc != PH7_OK ){
logg_web(PH7_ERROR, "%s: Error while registering foreign function %s()",
logg_web(FIFO_PH7, "%s: Error while registering foreign function %s()",
full_path, aFunc[i].zName);
}
}
@ -143,7 +143,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata)
rc = ph7_vm_exec(pVm,0);
if( rc != PH7_OK )
{
logg_web(PH7_ERROR, "%s: VM execution error", full_path);
logg_web(FIFO_PH7, "%s: VM execution error", full_path);
// Mark file as processed - this prevents the HTTP server
// from printing the raw PHP source code to the user
return 1;
@ -173,7 +173,7 @@ static int PH7_error_report(const void *pOutput, unsigned int nOutputLen,
// Log error message, strip trailing newline character if any
if(((const char*)pOutput)[nOutputLen-1] == '\n')
nOutputLen--;
logg_web(PH7_ERROR, "%.*s", nOutputLen, (const char*)pOutput);
logg_web(FIFO_PH7, "%.*s", nOutputLen, (const char*)pOutput);
return PH7_OK;
}
@ -181,7 +181,7 @@ void init_ph7(void)
{
if(ph7_init(&pEngine) != PH7_OK )
{
logg_web(PH7_ERROR, "Error while initializing a new PH7 engine instance");
logg_web(FIFO_PH7, "Error while initializing a new PH7 engine instance");
return;
}

View File

@ -85,7 +85,7 @@ static int redirect_root_handler(struct mg_connection *conn, void *input)
static int log_http_message(const struct mg_connection *conn, const char *message)
{
logg_web(HTTP_INFO, "HTTP info: %s", message);
logg_web(FIFO_CIVETWEB, "HTTP info: %s", message);
return 1;
}
@ -93,14 +93,14 @@ static int log_http_access(const struct mg_connection *conn, const char *message
{
// Only log when in API debugging mode
if(config.debug.api.v.b)
logg_web(HTTP_INFO, "ACCESS: %s", message);
logg_web(FIFO_CIVETWEB, "ACCESS: %s", message);
return 1;
}
void http_init(void)
{
logg_web(HTTP_INFO, "Initializing HTTP server on port %s", config.http.port.v.s);
logg_web(FIFO_CIVETWEB, "Initializing HTTP server on port %s", config.http.port.v.s);
/* Initialize the library */
unsigned int features = MG_FEATURES_FILES |
@ -111,7 +111,7 @@ void http_init(void)
if(mg_init_library(features) == 0)
{
logg_web(HTTP_INFO, "Initializing HTTP library failed!");
logg_web(FIFO_CIVETWEB, "Initializing HTTP library failed!");
return;
}