Skip to content

Commit

Permalink
Merge branch 'development' into new/webserver_headers
Browse files Browse the repository at this point in the history
  • Loading branch information
DL6ER committed Feb 22, 2025
2 parents 7ce1ef5 + e2297ea commit 5852f7e
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 33 deletions.
3 changes: 3 additions & 0 deletions patch/civetweb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ patch -p1 < patch/civetweb/0001-Register-CSRF-token-in-conn-request_info.patch
echo "Applying patch 0001-Log-debug-messages-to-webserver.log-when-debug.webse.patch"
patch -p1 < patch/civetweb/0001-Log-debug-messages-to-webserver.log-when-debug.webse.patch

echo "Applying patch 0001-Expose-bound-to-addresses-from-CivetWeb-to-the-front.patch"
patch -p1 < patch/civetweb/0001-Expose-bound-to-addresses-from-CivetWeb-to-the-front.patch

echo "ALL PATCHES APPLIED OKAY"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
From 0bee9e9f7942c5b73a715eaeadf5ab2d09a8c74d Mon Sep 17 00:00:00 2001
From: DL6ER <[email protected]>
Date: Sat, 22 Feb 2025 18:33:25 +0100
Subject: [PATCH] Expose bound-to addresses from CivetWeb to the frontend

Signed-off-by: DL6ER <[email protected]>
---
src/webserver/civetweb/civetweb.c | 1 +
src/webserver/civetweb/civetweb.h | 6 ++++++
2 files changed, 7 insertions(+)

diff --git a/src/webserver/civetweb/civetweb.c b/src/webserver/civetweb/civetweb.c
index fa908e54..66fcab01 100644
--- a/src/webserver/civetweb/civetweb.c
+++ b/src/webserver/civetweb/civetweb.c
@@ -3339,6 +3339,7 @@ mg_get_server_ports(const struct mg_context *ctx,
ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
+ memcpy(&ports[cnt].addr, &ctx->listening_sockets[i].lsa, sizeof(ports[cnt].addr));

if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
/* IPv4 */
diff --git a/src/webserver/civetweb/civetweb.h b/src/webserver/civetweb/civetweb.h
index eee958b4..6dcfa457 100644
--- a/src/webserver/civetweb/civetweb.h
+++ b/src/webserver/civetweb/civetweb.h
@@ -23,6 +23,8 @@
#ifndef CIVETWEB_HEADER_INCLUDED
#define CIVETWEB_HEADER_INCLUDED

+#include <netinet/in.h> /* Pi-hole extension */
+
#define CIVETWEB_VERSION "1.17"
#define CIVETWEB_VERSION_MAJOR (1)
#define CIVETWEB_VERSION_MINOR (17)
@@ -721,6 +723,10 @@ struct mg_server_port {
int _reserved2;
int _reserved3;
int _reserved4;
+ union {
+ struct sockaddr_in sa4; /* Pi-hole extension */
+ struct sockaddr_in6 sa6; /* Pi-hole extension */
+ } addr;
};

/* Legacy name */
--
2.43.0

2 changes: 1 addition & 1 deletion src/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ static void initConfig(struct config *conf)
conf->dns.ignoreLocalhost.c = validate_stub; // Only type-based checking

conf->dns.showDNSSEC.k = "dns.showDNSSEC";
conf->dns.showDNSSEC.h = "Should FTL should analyze and show internally generated DNSSEC queries?";
conf->dns.showDNSSEC.h = "Should FTL analyze and show internally generated DNSSEC queries?";
conf->dns.showDNSSEC.t = CONF_BOOL;
conf->dns.showDNSSEC.d.b = true;
conf->dns.showDNSSEC.c = validate_stub; // Only type-based checking
Expand Down
18 changes: 13 additions & 5 deletions src/config/setupVars.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,13 @@ static void get_revServer_from_setupVars(void)
char *domain_str = read_setupVarsconf("REV_SERVER_DOMAIN");
if(domain_str != NULL)
{
domain = strdup(domain_str);
trim_whitespace(domain);
if(strlen(domain_str) == 0)
log_info("setupVars.conf:REV_SERVER_DOMAIN -> Empty string, ignoring");
else
{
domain = strdup(domain_str);
trim_whitespace(domain);
}
}
else
log_info("setupVars.conf:REV_SERVER_DOMAIN -> Not set");
Expand All @@ -187,16 +192,19 @@ static void get_revServer_from_setupVars(void)
clearSetupVarsArray();

// Only add the entry if all values are present and active
if(cidr != NULL && target != NULL && domain != NULL)
if(cidr != NULL && target != NULL)
{
// Build comma-separated string of all values
// 9 = 3 commas, "true/false", and null terminator
char *old = calloc(strlen(cidr) + strlen(target) + strlen(domain) + 9, sizeof(char));
char *old = calloc(strlen(cidr) + strlen(target) + (domain != NULL ? strlen(domain) : 0) + 9, sizeof(char));
if(old != NULL)
{
// Add to new config
// active is always true as we only add active entries
sprintf(old, "%s,%s,%s,%s", active ? "true" : "false", cidr, target, domain);
if(domain != NULL && strlen(domain) > 0)
sprintf(old, "%s,%s,%s,%s", active ? "true" : "false", cidr, target, domain);
else
sprintf(old, "%s,%s,%s", active ? "true" : "false", cidr, target);
cJSON_AddItemToArray(config.dns.revServers.v.json, cJSON_CreateString(old));

// Parameter present in setupVars.conf
Expand Down
23 changes: 19 additions & 4 deletions src/config/validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDAT
return false;
}

// Check if it's in the form "IP HOSTNAME"
// Check if it's in the form "IP[ \t]HOSTNAME"
char *str = strdup(item->valuestring);
char *tmp = str;
char *ip = strsep(&tmp, " ");
char *ip = strsep(&tmp, " \t");

if(!ip || !*ip)
{
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not an IP address (\"%s\")",
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: found no first element (\"%s\")",
key, i, item->valuestring);
free(str);
return false;
Expand All @@ -74,8 +74,23 @@ bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDAT
// hostnames to come after the IP address
unsigned int hosts = 0;
char *host = NULL;
while((host = strsep(&tmp, " ")) != NULL)
while((host = strsep(&tmp, " \t")) != NULL)
{
// Skip extra whitespace/tabs
while(isspace((unsigned char)*host))
host++;

// Skip this entry if it's empty after trimming
// the whitespaces/tabs at the end of the line
if(strlen(host) == 0)
break;

// If this hostname is actually the start of a comment
// (first letter is '#'), skip parsing the rest of the
// entire line
if(host[0] == '#')
break;

if(!valid_domain(host, strlen(host), false))
{
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: invalid hostname (\"%s\")",
Expand Down
1 change: 1 addition & 0 deletions src/webserver/civetweb/civetweb.c
Original file line number Diff line number Diff line change
Expand Up @@ -3339,6 +3339,7 @@ mg_get_server_ports(const struct mg_context *ctx,
ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
memcpy(&ports[cnt].addr, &ctx->listening_sockets[i].lsa, sizeof(ports[cnt].addr));

if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
/* IPv4 */
Expand Down
6 changes: 6 additions & 0 deletions src/webserver/civetweb/civetweb.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#ifndef CIVETWEB_HEADER_INCLUDED
#define CIVETWEB_HEADER_INCLUDED

#include <netinet/in.h> /* Pi-hole extension */

#define CIVETWEB_VERSION "1.17"
#define CIVETWEB_VERSION_MAJOR (1)
#define CIVETWEB_VERSION_MINOR (17)
Expand Down Expand Up @@ -721,6 +723,10 @@ struct mg_server_port {
int _reserved2;
int _reserved3;
int _reserved4;
union {
struct sockaddr_in sa4; /* Pi-hole extension */
struct sockaddr_in6 sa6; /* Pi-hole extension */
} addr;
};

/* Legacy name */
Expand Down
62 changes: 41 additions & 21 deletions src/webserver/webserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ static struct serverports
bool is_secure :1;
bool is_redirect :1;
bool is_optional :1;
unsigned char protocol; // 1 = IPv4, 2 = IPv4+IPv6, 3 = IPv6
in_port_t port;
char addr[INET6_ADDRSTRLEN + 2]; // +2 for square brackets around IPv6 address
int port;
int protocol; // 1 = IPv4, 3 = IPv6
} server_ports[MAXPORTS] = { 0 };
static in_port_t https_port = 0;
/**
Expand Down Expand Up @@ -252,20 +253,37 @@ static void get_server_ports(void)
server_ports[i].is_secure = mgports[i].is_ssl;
server_ports[i].is_redirect = mgports[i].is_redirect;
server_ports[i].is_optional = mgports[i].is_optional;
// 1 = IPv4, 3 = IPv6 (can also be a combo-socker serving both),
// the documentation in civetweb.h is wrong
server_ports[i].protocol = mgports[i].protocol;

// Convert listening address to string
if(server_ports[i].protocol == 1)
inet_ntop(AF_INET, &mgports[i].addr.sa4.sin_addr, server_ports[i].addr, INET_ADDRSTRLEN);
else if(server_ports[i].protocol == 3)
{
char tmp[INET6_ADDRSTRLEN] = { 0 };
inet_ntop(AF_INET6, &mgports[i].addr.sa6.sin6_addr, tmp, INET6_ADDRSTRLEN);
// Enclose IPv6 address in square brackets
snprintf(server_ports[i].addr, sizeof(server_ports[i].addr), "[%s]", tmp);
}
else
log_warn("Unsupported protocol for port %d", mgports[i].port);

// Store (first) HTTPS port if not already set
if(mgports[i].is_ssl && https_port == 0)
https_port = mgports[i].port;

// Print port information
if(i == 0)
log_info("Web server ports:");
log_info(" - %d (HTTP%s, IPv%s%s%s)",
mgports[i].port, mgports[i].is_ssl ? "S" : "",
mgports[i].protocol == 1 ? "4" : (mgports[i].protocol == 3 ? "6" : "4+6"),
mgports[i].is_redirect ? ", redirecting" : "",
mgports[i].is_optional ? ", optional" : "");
log_info(" - %s:%d (HTTP%s, IPv%s%s%s)",
server_ports[i].addr,
server_ports[i].port,
server_ports[i].is_secure ? "S" : "",
server_ports[i].protocol == 1 ? "4" : "6",
server_ports[i].is_redirect ? ", redirecting" : "",
server_ports[i].is_optional ? ", optional" : "");

}
}
Expand All @@ -275,21 +293,13 @@ in_port_t __attribute__((pure)) get_https_port(void)
return https_port;
}

#define MAX_URL_LEN 255
unsigned short get_api_string(char **buf, const bool domain)
{
// Initialize buffer to empty string
size_t len = 0;
// First byte has the length of the first string
**buf = 0;
const char *domain_str = domain ? config.webserver.domain.v.s : "pi.hole";
size_t api_str_size = strlen(domain_str) + 20;

// Check if the string is too long for the TXT record
if(api_str_size > 255)
{
log_err("API URL too long for TXT record!");
return 0;
}

// TXT record format:
//
Expand All @@ -311,20 +321,30 @@ unsigned short get_api_string(char **buf, const bool domain)
continue;

// Reallocate additional memory for every port
if((*buf = realloc(*buf, (i+1)*api_str_size)) == NULL)
const size_t bufsz = (i + 1) * MAX_URL_LEN;
if((*buf = realloc(*buf, bufsz)) == NULL)
{
log_err("Failed to reallocate API URL buffer!");
return 0;
}
const size_t bufsz = (i+1)*api_str_size;

// Use appropriate domain
const char *addr = domain ? config.webserver.domain.v.s : server_ports[i].addr;

// If we bound to the wildcard address, substitute it with
// 127.0.0.1
if(strcmp(addr, "0.0.0.0") == 0)
addr = "127.0.0.1";
else if(strcasecmp(addr, "[::]") == 0)
addr = "[::1]";

// Append API URL to buffer
// We add this at buffer + 1 because the first byte is the
// length of the string, which we don't know yet
char *api_str = calloc(api_str_size, sizeof(char));
const ssize_t this_len = snprintf(api_str, bufsz - len - 1, "http%s://%s:%d/api/",
char *api_str = calloc(MAX_URL_LEN, sizeof(char));
const ssize_t this_len = snprintf(api_str, MAX_URL_LEN, "http%s://%s:%d/api/",
server_ports[i].is_secure ? "s" : "",
domain_str, server_ports[i].port);
addr, server_ports[i].port);
// Check if snprintf() failed
if(this_len < 0)
{
Expand Down
2 changes: 1 addition & 1 deletion test/pihole.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# Should FTL hide queries made by localhost?
ignoreLocalhost = false

# Should FTL should analyze and show internally generated DNSSEC queries?
# Should FTL analyze and show internally generated DNSSEC queries?
showDNSSEC = true

# Should FTL analyze *only* A and AAAA queries?
Expand Down
2 changes: 1 addition & 1 deletion test/test_suite.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@
@test "API addresses reported correctly by CHAOS TXT local.api.ftl" {
run bash -c 'dig CHAOS TXT local.api.ftl +short @127.0.0.1'
printf "dig (full): %s\n" "${lines[@]}"
[[ ${lines[0]} == '"http://pi.hole:80/api/" "https://pi.hole:443/api/"' ]]
[[ ${lines[0]} == '"http://127.0.0.1:80/api/" "https://127.0.0.1:443/api/" "http://[::1]:80/api/" "https://[::1]:443/api/"' ]]
}

@test "API addresses reported by CHAOS TXT api.ftl identical to domain.api.ftl" {
Expand Down

0 comments on commit 5852f7e

Please sign in to comment.