Merge branch 'master' into new/http

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2021-02-26 17:24:41 +01:00
commit 56f0e56b1f
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
66 changed files with 642 additions and 257 deletions

View File

@ -11,6 +11,6 @@
cmake_minimum_required(VERSION 2.8.12)
project(PIHOLE_FTL C)
set(DNSMASQ_VERSION pi-hole-2.83)
set(DNSMASQ_VERSION pi-hole-2.84)
add_subdirectory(src)

View File

@ -135,6 +135,8 @@ set(sources
main.h
overTime.c
overTime.h
procps.c
procps.h
regex.c
regex_r.h
resolve.c

View File

@ -194,7 +194,7 @@ int check_client_auth(struct ftl_conn *api)
if(config.debug & DEBUG_API)
{
char timestr[128];
get_timestr(timestr, auth_data[user_id].valid_until);
get_timestr(timestr, auth_data[user_id].valid_until, false);
logg("API: Recognized known user: user_id %i valid_until: %s remote_addr %s",
user_id, timestr, auth_data[user_id].remote_addr);
}
@ -516,7 +516,7 @@ int api_auth(struct ftl_conn *api)
if(config.debug & DEBUG_API && user_id > API_AUTH_UNAUTHORIZED)
{
char timestr[128];
get_timestr(timestr, auth_data[user_id].valid_until);
get_timestr(timestr, auth_data[user_id].valid_until, false);
logg("API: Registered new user: user_id %i valid_until: %s remote_addr %s (accepted due to %s)",
user_id, timestr, auth_data[user_id].remote_addr,
response_correct ? "correct response" : "empty password");

View File

@ -536,6 +536,25 @@ void read_FTLconf(void)
// WEBDOMAIN
getpath(fp, "WEBDOMAIN", "pi.hole", &httpsettings.webdomain);
// RATE_LIMIT
// defaults to: 1000 queries / 60 seconds
config.rate_limit.count = 1000;
config.rate_limit.interval = 60;
buffer = parse_FTLconf(fp, "RATE_LIMIT");
unsigned int count = 0, interval = 0;
if(buffer != NULL && sscanf(buffer, "%u/%u", &count, &interval) == 2)
{
config.rate_limit.count = count;
config.rate_limit.interval = interval;
}
if(config.rate_limit.count > 0)
logg(" RATE_LIMIT: Rate-limiting client making more than %u queries in %u second%s",
config.rate_limit.count, config.rate_limit.interval, config.rate_limit.interval == 1 ? "" : "s");
else
logg(" RATE_LIMIT: Disabled");
// Read DEBUG_... setting from pihole-FTL.conf
// This option should be the last one as it causes
// some rather verbose output into the log when
@ -601,7 +620,7 @@ static char *parse_FTLconf(FILE *fp, const char * key)
// Go to beginning of file
fseek(fp, 0L, SEEK_SET);
if(config.debug & DEBUG_EXTRA)
logg("initial: conflinebuffer = %p, keystr = %p, size = %zu", conflinebuffer, keystr, size);

View File

@ -53,10 +53,14 @@ typedef struct {
int dns_port;
unsigned int delay_startup;
unsigned int network_expire;
struct {
unsigned int count;
unsigned int interval;
} rate_limit;
enum debug_flags debug;
time_t DBinterval;
} ConfigStruct;
ASSERT_SIZEOF(ConfigStruct, 56, 48, 48);
ASSERT_SIZEOF(ConfigStruct, 64, 56, 56);
typedef struct {
const char* conf;

View File

@ -359,12 +359,13 @@ void DB_read_queries(void)
continue;
}
const int status = sqlite3_column_int(stmt, 3);
if(status < QUERY_UNKNOWN || status >= QUERY_STATUS_MAX)
const int status_int = sqlite3_column_int(stmt, 3);
if(status_int < QUERY_UNKNOWN || status_int >= QUERY_STATUS_MAX)
{
logg("FTL_db warn: STATUS should be within [%i,%i] but is %i", QUERY_UNKNOWN, QUERY_STATUS_MAX-1, status);
logg("FTL_db warn: STATUS should be within [%i,%i] but is %i", QUERY_UNKNOWN, QUERY_STATUS_MAX-1, status_int);
continue;
}
const enum query_status status = status_int;
const char * domainname = (const char *)sqlite3_column_text(stmt, 4);
if(domainname == NULL)
@ -387,12 +388,11 @@ void DB_read_queries(void)
continue;
}
const char *upstream = NULL;
const char *buffer = NULL;
int upstreamID = -1; // Default if not forwarded
// Determine upstreamID only when status == 2 (forwarded) as the
// field need not to be filled for other query status types
// Try to extract the upstream from the "forward" column if non-empty
if(sqlite3_column_bytes(stmt, 6) > 0 &&
(upstream = (const char *)sqlite3_column_text(stmt, 6)) != NULL)
(buffer = (const char *)sqlite3_column_text(stmt, 6)) != NULL)
{
// Get IP address and port of upstream destination
char serv_addr[INET6_ADDRSTRLEN] = { 0 };
@ -400,9 +400,9 @@ void DB_read_queries(void)
// We limit the number of bytes written into the serv_addr buffer
// to prevent buffer overflows. If there is no port available in
// the database, we skip extracting them and use the default port
sscanf(upstream, "%"xstr(INET6_ADDRSTRLEN)"[^#]#%u", serv_addr, &serv_port);
sscanf(buffer, "%"xstr(INET6_ADDRSTRLEN)"[^#]#%u", serv_addr, &serv_port);
serv_addr[INET6_ADDRSTRLEN-1] = '\0';
upstreamID = findUpstreamID(serv_addr, (in_port_t)serv_port, true);
upstreamID = findUpstreamID(serv_addr, (in_port_t)serv_port);
}
// Obtain IDs only after filtering which queries we want to keep
@ -522,7 +522,15 @@ void DB_read_queries(void)
break;
case QUERY_FORWARDED: // Forwarded
case QUERY_RETRIED: // (fall through)
case QUERY_RETRIED_DNSSEC: // (fall through)
counters->forwarded++;
upstreamsData *upstream = getUpstream(upstreamID, true);
if(upstream != NULL)
{
upstream->count++;
upstream->lastQuery = queryTimeStamp;
}
// Update overTime data structure
overTime[timeidx].forwarded++;
break;
@ -533,11 +541,11 @@ void DB_read_queries(void)
overTime[timeidx].cached++;
break;
case QUERY_RETRIED: // Retried query
case QUERY_RETRIED_DNSSEC: // fall through
case QUERY_IN_PROGRESS:
// Nothing to be done here
break;
case QUERY_STATUS_MAX:
default:
logg("Warning: Found unknown status %i in long term database!", status);
break;

View File

@ -65,7 +65,7 @@ int findQueryID(const int id)
return -1;
}
int findUpstreamID(const char * upstreamString, const in_port_t port, const bool count)
int findUpstreamID(const char * upstreamString, const in_port_t port)
{
// Go through already knows upstream servers and see if we used one of those
for(int upstreamID=0; upstreamID < counters->upstreams; upstreamID++)
@ -78,13 +78,7 @@ int findUpstreamID(const char * upstreamString, const in_port_t port, const bool
continue;
if(strcmp(getstr(upstream->ippos), upstreamString) == 0 && upstream->port == port)
{
if(count)
{
upstream->count++;
}
return upstreamID;
}
}
// This upstream server is not known
// Store ID
@ -105,10 +99,7 @@ int findUpstreamID(const char * upstreamString, const in_port_t port, const bool
// Set magic byte
upstream->magic = MAGICBYTE;
// Initialize its counter
if(count)
upstream->count = 1;
else
upstream->count = 0;
upstream->count = 0;
// Save upstream destination IP address
upstream->ippos = addstr(upstreamString);
upstream->failed = 0;
@ -591,6 +582,8 @@ const char * __attribute__ ((pure)) get_query_status_str(const queriesData *quer
return "RETRIED";
case QUERY_RETRIED_DNSSEC:
return "RETRIED_DNSSEC";
case QUERY_IN_PROGRESS:
return "IN_PROGRESS";
case QUERY_STATUS_MAX:
default:
return "STATUS_MAX";

View File

@ -84,6 +84,7 @@ typedef struct {
int blockedcount;
int aliasclient_id;
unsigned int id;
unsigned int rate_limit;
unsigned int numQueriesARP;
int overTime[OVERTIME_SLOTS];
size_t groupspos;
@ -95,7 +96,7 @@ typedef struct {
} clientsData;
// ARM needs alignment to 8-byte boundary
ASSERT_SIZEOF(clientsData, 688, 668, 672);
ASSERT_SIZEOF(clientsData, 696, 668, 668);
typedef struct {
unsigned char magic;
@ -118,7 +119,7 @@ ASSERT_SIZEOF(DNSCacheData, 16, 16, 16);
void strtolower(char *str);
int findQueryID(const int id);
int findUpstreamID(const char * upstream, const in_port_t port, const bool count);
int findUpstreamID(const char * upstream, const in_port_t port);
int findDomainID(const char *domain, const bool count);
int findClientID(const char *client, const bool count, const bool aliasclient);
int findCacheID(int domainID, int clientID, enum query_types query_type);

View File

@ -134,7 +134,7 @@ static int get_hardware_address(const int sock, const char *interface_name, unsi
logg_sameline("Hardware address of this interface: ");
for (uint8_t i = 0; i < 6; ++i)
logg_sameline("%02x%s", mac[i], i < 5 ? ":" : "");
logg("");
logg(" ");
#endif
return true;
}
@ -201,14 +201,8 @@ static bool send_dhcp_discover(const int sock, const uint32_t xid, const char *i
discover_packet.options[5] = '\x01'; // DHCP message option length in bytes
discover_packet.options[6] = 1; // DHCP message type code for DHCPDISCOVER
// Request a lease with validity of 1 second
discover_packet.options[7] = 51; // Lease time type option identifier
discover_packet.options[8] = '\x04'; // DHCP message option length in bytes
const uint32_t lease_time = htonl(1);
memcpy(&discover_packet.options[9], &lease_time, sizeof(lease_time));
// Place end option at the end of the options
discover_packet.options[13] = 255;
discover_packet.options[7] = 255;
// send the DHCPDISCOVER packet to the specified address
struct sockaddr_in target;
@ -452,12 +446,12 @@ static bool get_dhcp_offer(const int sock, const uint32_t xid, const char *iface
if(!receive_dhcp_packet(&offer_packet, sizeof(offer_packet), iface, sock, start_time, &source))
continue;
#if DEBUG
#ifdef DEBUG
else
responses++;
#endif
#if DEBUG
#ifdef DEBUG
logg(" DHCPOFFER XID: %lu (0x%X)", (unsigned long) ntohl(offer_packet.xid), ntohl(offer_packet.xid));
#endif
@ -555,19 +549,20 @@ static void *dhcp_discover_iface(void *args)
srand(time(NULL));
const uint32_t xid = random();
#ifdef PROBE_LOCAL
// Probe a local server listining on this interface
// send DHCPDISCOVER packet to interface address
struct sockaddr_in ifaddr = { 0 };
memcpy(&ifaddr, ((struct ifaddrs*)args)->ifa_addr, sizeof(ifaddr));
send_dhcp_discover(dhcp_socket, xid, iface, ifaddr.sin_addr.s_addr);
#endif
#ifdef PROBE_BCAST
// Probe distant servers
// send DHCPDISCOVER packet to broadcast address
send_dhcp_discover(dhcp_socket, xid, iface, mac, INADDR_BROADCAST);
#endif
if(strcmp(iface, "lo") == 0)
{
// Probe a local server listening on this interface
// Send DHCPDISCOVER packet to interface address
struct sockaddr_in ifaddr = { 0 };
memcpy(&ifaddr, ((struct ifaddrs*)args)->ifa_addr, sizeof(ifaddr));
send_dhcp_discover(dhcp_socket, xid, iface, mac, ifaddr.sin_addr.s_addr);
}
else
{
// Probe distant servers
// Send DHCPDISCOVER packet to broadcast address
send_dhcp_discover(dhcp_socket, xid, iface, mac, INADDR_BROADCAST);
}
// wait for a DHCPOFFER packet
get_dhcp_offer(dhcp_socket, xid, iface, mac);
@ -615,13 +610,8 @@ int run_dhcp_discover(void)
int tid = 0;
while(tmp != NULL && tid < MAXTHREADS)
{
#ifdef PROBE_LOCAL
// Create a thread for interfaces of type AF_INET (IPv4 sockets)
if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
#else
// Create a thread for interfaces of type AF_PACKET
if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET)
#endif
{
if(pthread_create(&scanthread[tid], &attr, dhcp_discover_iface, tmp ) != 0)
{

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -122,8 +122,8 @@ HAVE_AUTH
define this to include the facility to act as an authoritative DNS
server for one or more zones.
HAVE_NETTLEHASH
include just hash function from nettle, but no DNSSEC.
HAVE_CRYPTOHASH
include just hash function from crypto library, but no DNSSEC.
HAVE_DNSSEC
include DNSSEC validator.
@ -192,7 +192,7 @@ RESOLVFILE
/* #define HAVE_IDN */
/* #define HAVE_LIBIDN2 */
/* #define HAVE_CONNTRACK */
/* #define HAVE_NETTLEHASH */
/* #define HAVE_CRYPTOHASH */
/* #define HAVE_DNSSEC */
@ -426,10 +426,10 @@ static char *compile_opts =
"no-"
#endif
"auth "
#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC)
#if !defined(HAVE_CRYPTOHASH) && !defined(HAVE_DNSSEC)
"no-"
#endif
"nettlehash "
"cryptohash "
#ifndef HAVE_DNSSEC
"no-"
#endif

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -27,7 +27,7 @@
#endif
#endif
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
#include <nettle/nettle-meta.h>
#include <nettle/bignum.h>

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -392,8 +392,8 @@ int main_dnsmasq (int argc, char **argv)
if (daemon->port != 0)
{
cache_init();
blockdata_init();
hash_questions_init();
}
#ifdef HAVE_INOTIFY

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2020 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2021 Simon Kelley"
/* We do defines that influence behavior of stdio.h, so complain
if included too early. */
@ -157,7 +157,11 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
#include <priv.h>
#endif
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
/* Backwards compat with 2.83 */
#if defined(HAVE_NETTLEHASH)
# define HAVE_CRYPTOHASH
#endif
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
# include <nettle/nettle-meta.h>
#endif
@ -666,6 +670,7 @@ struct frec {
union mysockaddr source;
union all_addr dest;
unsigned int iface, log_id;
int fd;
unsigned short orig_id;
struct frec_src *next;
} frec_src;
@ -673,7 +678,7 @@ struct frec {
struct randfd *rfd4;
struct randfd *rfd6;
unsigned short new_id;
int fd, forwardall, flags;
int forwardall, flags;
time_t time;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
@ -1265,6 +1270,7 @@ size_t filter_rrsigs(struct dns_header *header, size_t plen);
int setup_timestamp(void);
/* hash_questions.c */
void hash_questions_init(void);
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name);
/* crypto.c */

View File

@ -334,37 +334,64 @@ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (!CHECK_LEN(header, state2.ip, plen, rdlen2))
return rrsetidx; /* short packet */
state2.end = state2.ip + rdlen2;
while (1)
{
int ok1, ok2;
ok1 = get_rdata(header, plen, &state1);
ok2 = get_rdata(header, plen, &state2);
if (!ok1 && !ok2)
/* If the RR has no names in it then canonicalisation
is the identity function and we can compare
the RRs directly. If not we compare the
canonicalised RRs one byte at a time. */
if (*rr_desc == (u16)-1)
{
int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1;
int cmp = memcmp(state1.ip, state2.ip, rdmin);
if (cmp > 0 || (cmp == 0 && rdlen1 > rdmin))
{
unsigned char *tmp = rrset[i+1];
rrset[i+1] = rrset[i];
rrset[i] = tmp;
swap = 1;
}
else if (cmp == 0 && (rdlen1 == rdlen2))
{
/* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
for (j = i+1; j < rrsetidx-1; j++)
rrset[j] = rrset[j+1];
rrsetidx--;
i--;
break;
}
else if (ok1 && (!ok2 || *state1.op > *state2.op))
{
unsigned char *tmp = rrset[i+1];
rrset[i+1] = rrset[i];
rrset[i] = tmp;
swap = 1;
break;
}
else if (ok2 && (!ok1 || *state2.op > *state1.op))
break;
/* arrive here when bytes are equal, go round the loop again
and compare the next ones. */
}
else
/* Comparing canonicalised RRs, byte-at-a-time. */
while (1)
{
int ok1, ok2;
ok1 = get_rdata(header, plen, &state1);
ok2 = get_rdata(header, plen, &state2);
if (!ok1 && !ok2)
{
/* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
for (j = i+1; j < rrsetidx-1; j++)
rrset[j] = rrset[j+1];
rrsetidx--;
i--;
break;
}
else if (ok1 && (!ok2 || *state1.op > *state2.op))
{
unsigned char *tmp = rrset[i+1];
rrset[i+1] = rrset[i];
rrset[i] = tmp;
swap = 1;
break;
}
else if (ok2 && (!ok1 || *state2.op > *state1.op))
break;
/* arrive here when bytes are equal, go round the loop again
and compare the next ones. */
}
}
} while (swap);

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -391,8 +391,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
new->dest = *dst_addr;
new->log_id = daemon->log_id;
new->iface = dst_iface;
new->fd = udpfd;
}
// Pi-hole modification
FTL_query_in_progress(daemon->log_id);
return 1;
}
@ -415,8 +419,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.dest = *dst_addr;
forward->frec_src.iface = dst_iface;
forward->frec_src.next = NULL;
forward->frec_src.fd = udpfd;
forward->new_id = get_id();
forward->fd = udpfd;
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0;
forward->flags = fwd_flags;
@ -1329,6 +1333,9 @@ void reply_query(int fd, int family, time_t now)
}
#endif
// Pi-hole modification
int first_ID = -1;
for (src = &forward->frec_src; src; src = src->next)
{
header->id = htons(src->orig_id);
@ -1337,7 +1344,7 @@ void reply_query(int fd, int family, time_t now)
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source);
#endif
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&src->source, &src->dest, src->iface);
if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src)
@ -1346,6 +1353,8 @@ void reply_query(int fd, int family, time_t now)
daemon->log_source_addr = &src->source;
log_query(F_UPSTREAM, "query", NULL, "duplicate");
}
/* Pi-hole modification */
FTL_duplicate_reply(src->log_id, &first_ID);
}
}
@ -1700,6 +1709,10 @@ void receive_query(struct listener *listen, time_t now)
FTL_get_blocking_metadata(&addrp, &flags);
log_query(flags, daemon->namebuff, addrp, (char*)blockingreason);
n = setup_reply(header, n, addrp, flags, daemon->local_ttl);
// The pseudoheader may contain important information such as EDNS0 version important for
// some DNS resolvers (such as systemd-resolved) to work properly. We should not discard them.
if (have_pseudoheader)
n = add_pseudoheader(header, n, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, n, (union mysockaddr*)&source_addr, &dst_addr, if_index);
return;
}
@ -2091,6 +2104,10 @@ unsigned char *tcp_request(int confd, time_t now,
FTL_get_blocking_metadata(&addrp, &flags);
log_query(flags, daemon->namebuff, addrp, (char*)blockingreason);
m = setup_reply(header, size, addrp, flags, daemon->local_ttl);
// The pseudoheader may contain important information such as EDNS0 version important for
// some DNS resolvers (such as systemd-resolved) to work properly. We should not discard them.
if (have_pseudoheader)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
else
{

View File

@ -28,28 +28,28 @@
#include "dnsmasq.h"
#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH)
#if defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH)
static const struct nettle_hash *hash;
static void *ctx;
static unsigned char *digest;
void hash_questions_init(void)
{
if (!(hash = hash_find("sha256")))
die(_("Failed to create SHA-256 hash object"), NULL, EC_MISC);
ctx = safe_malloc(hash->context_size);
digest = safe_malloc(hash->digest_size);
}
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
{
int q;
unsigned char *p = (unsigned char *)(header+1);
const struct nettle_hash *hash;
void *ctx;
unsigned char *digest;
if (!(hash = hash_find("sha256")) || !hash_init(hash, &ctx, &digest))
{
/* don't think this can ever happen. */
static unsigned char dummy[HASH_SIZE];
static int warned = 0;
if (!warned)
my_syslog(LOG_ERR, _("Failed to create SHA-256 hash object"));
warned = 1;
return dummy;
}
hash->init(ctx);
for (q = ntohs(header->qdcount); q != 0; q--)
{
char *cp, c;
@ -74,7 +74,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
return digest;
}
#else /* HAVE_DNSSEC */
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
typedef unsigned char BYTE; // 8-bit byte
@ -91,6 +91,9 @@ static void sha256_init(SHA256_CTX *ctx);
static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
static void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
void hash_questions_init(void)
{
}
unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name)
{

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
@ -20,7 +20,7 @@
#include <setjmp.h>
/* Pi-hole modification */
extern char *get_FTL_version(void);
#include "../log.h"
/************************/
static volatile int mem_recover = 0;
@ -5059,7 +5059,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
/************************/
#endif
/******** Pi-hole modification ********/
add_txt("version.FTL", get_FTL_version(), 0 );
add_txt("version.FTL", (char*)get_FTL_version(), 0 );
/**************************************/
while (1)

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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

View File

@ -191,8 +191,8 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
// as something along the CNAME path hit the whitelist
if(!query->flags.whitelisted)
{
query_blocked(query, domain, client, QUERY_BLACKLIST);
force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, QUERY_BLACKLIST);
return true;
}
break;
@ -211,8 +211,8 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
// as sometving along the CNAME path hit the whitelist
if(!query->flags.whitelisted)
{
query_blocked(query, domain, client, QUERY_GRAVITY);
force_next_DNS_reply = dns_cache->force_reply;
query_blocked(query, domain, client, QUERY_GRAVITY);
return true;
}
break;
@ -533,19 +533,9 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
return false;
}
// Lock shared memory
lock_shm();
// Ensure we have enough space in the queries struct
memory_check(QUERIES);
const int queryID = counters->queries;
// If domain is "pi.hole" we skip this query
if(strcasecmp(name, "pi.hole") == 0)
{
unlock_shm();
return false;
}
// Convert domain to lower case
char *domainString = strdup(name);
@ -575,10 +565,50 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
(strcmp(clientIP, "127.0.0.1") == 0 || strcmp(clientIP, "::1") == 0))
{
free(domainString);
return false;
}
// Lock shared memory
lock_shm();
// Find client IP
const int clientID = findClientID(clientIP, true, false);
// Get client pointer
clientsData* client = getClient(clientID, true);
if(client == NULL)
{
// Encountered memory error, skip query
// Free allocated memory
free(domainString);
// Release thread lock
unlock_shm();
return false;
}
// Check rate-limit for this client
if(config.rate_limit.count > 0 &&
++client->rate_limit > config.rate_limit.count)
{
if(config.debug & DEBUG_QUERIES)
{
logg("Rate-limiting %s %s query \"%s\" from %s:%s",
proto == TCP ? "TCP" : "UDP",
types, domainString, next_iface, clientIP);
}
// Block this query
force_next_DNS_reply = REFUSED;
// Do not further process this query, Pi-hole has never seen it
unlock_shm();
return true;
}
// Ensure we have enough space in the queries struct
memory_check(QUERIES);
const int queryID = counters->queries;
// Log new query if in debug mode
if(config.debug & DEBUG_QUERIES)
{
@ -608,9 +638,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
// Go through already knows domains and see if it is one of them
const int domainID = findDomainID(domainString, true);
// Go through already knows clients and see if it is one of them
const int clientID = findClientID(clientIP, true, false);
// Save everything
queriesData* query = getQuery(queryID, false);
if(query == NULL)
@ -664,19 +691,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
// Update overTime data
overTime[timeidx].total++;
// Get client pointer
clientsData* client = getClient(clientID, true);
if(client == NULL)
{
// Encountered memory error, skip query
logg("WARN: No memory available, skipping query analysis");
// Free allocated memory
free(domainString);
// Release thread lock
unlock_shm();
return false;
}
// Update overTime data structure with the new client
change_clientcount(client, 0, 0, timeidx, 1);
@ -763,8 +777,8 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, const char* file, const int line)
{
// Check first if we need to force our reply to something different than the
// default/configured blocking mode For instance, we need to force NXDOMAIN
// for intercepted _esni.* queries
// default/configured blocking mode. For instance, we need to force NXDOMAIN
// for intercepted _esni.* queries.
if(force_next_DNS_reply == NXDOMAIN)
{
*flags = F_NXDOMAIN;
@ -772,6 +786,14 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con
force_next_DNS_reply = 0u;
return;
}
else if(force_next_DNS_reply == REFUSED)
{
// Empty flags result in REFUSED
*flags = 0;
// Reset DNS reply forcing
force_next_DNS_reply = 0u;
return;
}
// Add flags according to current blocking mode
// We bit-add here as flags already contains either F_IPV4 or F_IPV6
@ -869,12 +891,18 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct ser
// Get ID of upstream destination, create new upstream record
// if not found in current data structure
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort, true);
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort);
query->upstreamID = upstreamID;
upstreamsData *upstream = getUpstream(upstreamID, true);
if(upstream != NULL)
{
upstream->count++;
upstream->lastQuery = double_time();
}
// Update counter for forwarded queries
counters->forwarded++;
// Get time index for this query
const unsigned int timeidx = query->timeidx;
@ -926,9 +954,6 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const struct ser
// Update overTime data
overTime[timeidx].forwarded++;
// Update counter for forwarded queries
counters->forwarded++;
struct timeval request;
gettimeofday(&request, 0);
query->forwardresponse = converttimeval(request);
@ -1275,9 +1300,6 @@ static void query_externally_blocked(const int queryID, const enum query_status
return;
}
// Get time index
const unsigned int timeidx = query->timeidx;
// If query is already known to be externally blocked,
// then we have nothing to do here
if(query->status == QUERY_EXTERNAL_BLOCKED_IP ||
@ -1285,18 +1307,6 @@ static void query_externally_blocked(const int queryID, const enum query_status
query->status == QUERY_EXTERNAL_BLOCKED_NXRA)
return;
// Correct counters if necessary ...
if(query->status == QUERY_FORWARDED)
{
counters->forwarded--;
overTime[timeidx].forwarded--;
// Get forward pointer
upstreamsData* upstream = getUpstream(query->upstreamID, true);
if(upstream != NULL)
upstream->count--;
}
// Mark query as blocked
domainsData* domain = getDomain(query->domainID, true);
clientsData* client = getClient(query->clientID, true);
@ -1444,6 +1454,12 @@ static void query_blocked(queriesData* query, domainsData* domain, clientsData*
else if(query->status == QUERY_FORWARDED)
{
counters->forwarded--;
overTime[query->timeidx].forwarded--;
// Get forward pointer
upstreamsData* upstream = getUpstream(query->upstreamID, true);
if(upstream != NULL)
upstream->count--;
}
else if(query->status == QUERY_CACHE)
{
@ -1679,7 +1695,7 @@ static void save_reply_type(const unsigned int flags, const union all_addr *addr
queriesData* query, const struct timeval response)
{
// Iterate through possible values
if(flags & F_NEG)
if(flags & F_NEG || force_next_DNS_reply == NXDOMAIN)
{
if(flags & F_NXDOMAIN)
{
@ -1711,15 +1727,15 @@ static void save_reply_type(const unsigned int flags, const union all_addr *addr
// TXT query
query->reply = REPLY_RRNAME;
}
else if(flags & F_RCODE && addr != NULL)
else if((flags & F_RCODE && addr != NULL) || force_next_DNS_reply == REFUSED)
{
const unsigned int rcode = addr->log.rcode;
if(rcode == REFUSED)
if((addr != NULL && addr->log.rcode == REFUSED)
|| force_next_DNS_reply == REFUSED )
{
// REFUSED query
query->reply = REPLY_REFUSED;
}
else if(rcode == SERVFAIL)
else if(addr != NULL && addr->log.rcode == SERVFAIL)
{
// SERVFAIL query
query->reply = REPLY_SERVFAIL;
@ -1890,7 +1906,7 @@ void FTL_forwarding_retried(const struct server *serv, const int oldID, const in
strtolower(upstreamIP);
// Get upstream ID
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort, false);
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort);
// Possible debugging information
if(config.debug & DEBUG_QUERIES)
@ -1908,7 +1924,7 @@ void FTL_forwarding_retried(const struct server *serv, const int oldID, const in
// Search for corresponding query identified by ID
// Retried DNSSEC queries are ignored, we have to flag themselves (newID)
// Retried normal queries take over, we have to flat the original query (oldID)
// Retried normal queries take over, we have to flag the original query (oldID)
const int queryID = findQueryID(dnssec ? newID : oldID);
if(queryID >= 0)
{
@ -2188,3 +2204,110 @@ bool FTL_unlink_DHCP_lease(const char *ipaddr)
return true;
}
void FTL_query_in_progress(const int id)
{
// Query (possibly from new source), but the same query may be in
// progress from another source.
// Lock shared memory
lock_shm();
// Search for corresponding query identified by ID
const int queryID = findQueryID(id);
if(queryID < 0)
{
// This may happen e.g. if the original query was an unhandled query type
unlock_shm();
return;
}
// Get query pointer
queriesData* query = getQuery(queryID, true);
if(query == NULL)
{
// Memory error, skip this DNSSEC details
unlock_shm();
return;
}
// Debug logging
if(config.debug & DEBUG_QUERIES)
{
// Get domain pointer
const domainsData* domain = getDomain(query->domainID, true);
if(domain != NULL)
{
logg("**** query for %s is already in progress (ID %i)", getstr(domain->domainpos), id);
}
}
// Store status
query->status = QUERY_IN_PROGRESS;
// Unlock shared memory
unlock_shm();
}
void FTL_duplicate_reply(const int id, int *firstID)
{
// Reply to duplicated query
// Check if we can process thes duplicated queries at all
if(*firstID == -2)
return;
// Lock shared memory
lock_shm();
// Search for corresponding query identified by ID
const int queryID = findQueryID(id);
if(queryID < 0)
{
// This may happen e.g. if the original query was an unhandled query type
unlock_shm();
*firstID = -2;
return;
}
if(*firstID == -1)
{
// This is not yet a duplicate, we just store the ID
// of the successful reply here so we can get it quicker
// during the next loop iterations
unlock_shm();
*firstID = queryID;
return;
}
// Get query pointer of duplicate reply
queriesData* duplicated_query = getQuery(queryID, true);
const queriesData* source_query = getQuery(*firstID, true);
if(duplicated_query == NULL || source_query == NULL)
{
// Memory error, skip this duplicate
unlock_shm();
return;
}
// Debug logging
if(config.debug & DEBUG_QUERIES)
{
logg("**** query %d is duplicate of %d", queryID, *firstID);
}
// Copy relevant information over
duplicated_query->reply = source_query->reply;
duplicated_query->dnssec = source_query->dnssec;
duplicated_query->flags.complete = true;
// The original query may have been blocked during CNAME inspection,
// correct status in this case
if(source_query->status != QUERY_FORWARDED)
duplicated_query->status = source_query->status;
duplicated_query->CNAME_domainID = source_query->CNAME_domainID;
// Unlock shared memory
unlock_shm();
}

View File

@ -53,6 +53,8 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con
bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const char* file, const int line);
unsigned int FTL_extract_question_flags(struct dns_header *header, const size_t qlen);
void FTL_query_in_progress(const int id);
void FTL_duplicate_reply(const int id, int *firstID);
void FTL_dnsmasq_reload(void);
void FTL_fork_and_bind_sockets(struct passwd *ent_pw);

View File

@ -42,6 +42,7 @@ enum query_status {
QUERY_BLACKLIST_CNAME,
QUERY_RETRIED,
QUERY_RETRIED_DNSSEC,
QUERY_IN_PROGRESS,
QUERY_STATUS_MAX
} __attribute__ ((packed));

View File

@ -23,29 +23,46 @@
bool doGC = false;
time_t lastGCrun = 0;
static void reset_rate_limiting(void)
{
for(int clientID = 0; clientID < counters->clients; clientID++)
{
clientsData *client = getClient(clientID, true);
if(client != NULL)
client->rate_limit = 0;
}
}
void *GC_thread(void *val)
{
// Set thread name
prctl(PR_SET_NAME,"housekeeper",0,0,0);
// Save timestamp as we do not want to store immediately
// to the database
lastGCrun = time(NULL) - time(NULL)%GCinterval;
// Remember when we last ran the actions
time_t lastGCrun = time(NULL) - time(NULL)%GCinterval;
time_t lastRateLimitCleaner = time(NULL);
while(!killed)
{
if(time(NULL) - GCdelay - lastGCrun >= GCinterval || doGC)
const time_t now = time(NULL);
if((unsigned int)(now - lastRateLimitCleaner) >= config.rate_limit.interval)
{
lastRateLimitCleaner = now;
lock_shm();
reset_rate_limiting();
unlock_shm();
}
if(now - GCdelay - lastGCrun >= GCinterval || doGC)
{
doGC = false;
// Update lastGCrun timer
lastGCrun = time(NULL) - GCdelay - (time(NULL) - GCdelay)%GCinterval;
lastGCrun = now - GCdelay - (now - GCdelay)%GCinterval;
// Lock FTL's data structure, since it is likely that it will be changed here
// Requests should not be processed/answered when data is about to change
lock_shm();
// Get minimum time stamp to keep
time_t mintime = (time(NULL) - GCdelay) - MAXLOGAGE*3600;
time_t mintime = (now - GCdelay) - MAXLOGAGE*3600;
// Align to the start of the next hour. This will also align with
// the oldest overTime interval after GC is done.
@ -56,7 +73,7 @@ void *GC_thread(void *val)
{
timer_start(GC_TIMER);
char timestring[84] = "";
get_timestr(timestring, mintime);
get_timestr(timestring, mintime, false);
logg("GC starting, mintime: %s (%llu)", timestring, (long long)mintime);
}
@ -128,6 +145,11 @@ void *GC_thread(void *val)
if(client != NULL)
change_clientcount(client, 0, -1, -1, 0);
break;
case QUERY_IN_PROGRESS:
// Nothing to be done here, this was a duplicated query. It
// wasn't forwarded on its own to save some traffic (and
// reduce the attack surface for cache spoofing)
break;
case QUERY_STATUS_MAX: // fall through
default:
/* That cannot happen */

View File

@ -112,18 +112,27 @@ double double_time(void)
// The size of 84 bytes has been carefully selected for all possible timestamps
// to always fit into the available space without buffer overflows
void get_timestr(char * const timestring, const time_t timein)
void get_timestr(char * const timestring, const time_t timein, const bool millis)
{
struct tm tm;
localtime_r(&timein, &tm);
struct timeval tv;
gettimeofday(&tv, NULL);
const int millisec = tv.tv_usec/1000;
if(millis)
{
struct timeval tv;
gettimeofday(&tv, NULL);
const int millisec = tv.tv_usec/1000;
sprintf(timestring,"%d-%02d-%02d %02d:%02d:%02d.%03i",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, millisec);
sprintf(timestring,"%d-%02d-%02d %02d:%02d:%02d.%03i",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, millisec);
}
else
{
sprintf(timestring,"%d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
}
void _FTL_log(const bool newline, const char *func, const char *file, const int line, const char *format, ...)
@ -142,7 +151,7 @@ void _FTL_log(const bool newline, const char *func, const char *file, const int
pthread_mutex_lock(&FTL_log_lock);
// Get human-readable time
get_timestr(timestring, time(NULL));
get_timestr(timestring, time(NULL), true);
// Get and log PID of current process to avoid ambiguities when more than one
// pihole-FTL instance is logging into the same file
@ -241,7 +250,7 @@ void __attribute__ ((format (gnu_printf, 2, 3))) logg_web(enum web_code code, co
pthread_mutex_lock(&web_log_lock);
// Get human-readable time
get_timestr(timestring, time(NULL));
get_timestr(timestring, time(NULL), true);
// Get and log PID of current process to avoid ambiguities when more than one
// pihole-FTL instance is logging into the same file

View File

@ -23,7 +23,7 @@ void format_time(char buffer[42], unsigned long seconds, double milliseconds);
const char *get_FTL_version(void) __attribute__ ((malloc));
void log_FTL_version(bool crashreport);
double double_time(void);
void get_timestr(char * const timestring, const time_t timein);
void get_timestr(char * const timestring, const time_t timein, const bool millis);
void logg_web(enum web_code code, const char* format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
const char *get_ordinal_suffix(unsigned int number) __attribute__ ((const));

View File

@ -25,6 +25,7 @@
#include "timers.h"
// http_terminate()
#include "webserver/webserver.h"
#include "procps.h"
char * username;
bool needGC = false;
@ -56,16 +57,17 @@ int main (int argc, char* argv[])
// We handle real-time signals later (after dnsmasq has forked)
handle_SIGSEGV();
// Process pihole-FTL.conf
read_FTLconf();
// Initialize shared memory - replace possibly existing files
// Initialize shared memory
if(!init_shmem(true))
{
logg("Initialization of shared memory failed.");
check_running_FTL();
return EXIT_FAILURE;
}
// Process pihole-FTL.conf
read_FTLconf();
// pihole-FTL should really be run as user "pihole" to not mess up with file permissions
// print warning otherwise
if(strcmp(username, "pihole") != 0)

133
src/procps.c Normal file
View File

@ -0,0 +1,133 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2021 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* /proc system subroutines
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "FTL.h"
#include "procps.h"
#include "log.h"
#include <dirent.h>
// getpid()
#include <unistd.h>
#define PROCESS_NAME "pihole-FTL"
static bool get_process_name(const pid_t pid, char name[128])
{
if(pid == 0)
{
strcpy(name, "init");
return true;
}
// Try to open comm file
char filename[sizeof("/proc/%u/task/%u/comm") + sizeof(int)*3 * 2];
snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
FILE *f = fopen(filename, "r");
if(f == NULL)
return false;
// Read name from opened file
if(fscanf(f, "%128s", name) != 1)
false;
fclose(f);
return true;
}
static bool get_process_ppid(const pid_t pid, pid_t *ppid)
{
// Try to open status file
char filename[sizeof("/proc/%u/task/%u/comm") + sizeof(int)*3 * 2];
snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
FILE *f = fopen(filename, "r");
if(f == NULL)
return false;
// Read comm from opened file
char buffer[128];
while(fgets(buffer, sizeof(buffer), f) != NULL)
{
if(sscanf(buffer, "PPid: %d\n", ppid) == 1)
break;
}
fclose(f);
return true;
}
static bool get_process_creation_time(const pid_t pid, char timestr[84])
{
// Try to open comm file
char filename[sizeof("/proc/%u/task/%u/comm") + sizeof(int)*3 * 2];
snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
struct stat st;
if(stat(filename, &st) < 0)
return false;
get_timestr(timestr, st.st_ctim.tv_sec, false);
return true;
}
void check_running_FTL(void)
{
//pid_t pid;
DIR *dirPos;
struct dirent *entry;
// Open /proc
errno = 0;
if ((dirPos = opendir("/proc")) == NULL)
{
logg("Dailed to access /proc: %s", strerror(errno));
return;
}
// Loop over entries in /proc
// This is much more efficient than iterating over all possible PIDs
while ((entry = readdir(dirPos)) != NULL)
{
// We are only interested in subdirectories of /proc
if(entry->d_type != DT_DIR)
continue;
// We are only interested in PID subdirectories
if(entry->d_name[0] < '0' || entry->d_name[0] > '9')
continue;
// Extract PID
const pid_t pid = strtol(entry->d_name, NULL, 10);
// Skip our own process
if(pid == getpid())
continue;
// Get process name
char name[128] = { 0 };
if(!get_process_name(pid, name))
continue;
// Get parent process ID (PPID)
pid_t ppid;
if(!get_process_ppid(pid, &ppid))
continue;
char ppid_name[128] = { 0 };
if(!get_process_name(ppid, ppid_name))
continue;
char timestr[84] = { 0 };
get_process_creation_time(pid, timestr);
// Log this process if it is a duplicate of us
if(strcasecmp(name, PROCESS_NAME) == 0)
logg("---> %s is already running as PID %d (started %s, child of PID %i (%s))",
PROCESS_NAME, pid, timestr, ppid, ppid_name);
}
closedir(dirPos);
}

15
src/procps.h Normal file
View File

@ -0,0 +1,15 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2021 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* /proc system prototypes
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#ifndef PROCPS_H
#define PROCPS_H
void check_running_FTL(void);
#endif // POCPS_H

View File

@ -81,6 +81,7 @@ static ShmSettings *shmSettings = NULL;
static int pagesize;
static unsigned int local_shm_counter = 0;
static size_t used_shmem = 0u;
static size_t get_optimal_object_size(const size_t objsize, const size_t minsize);
static int get_dev_shm_usage(char buffer[64])
@ -106,13 +107,19 @@ static int get_dev_shm_usage(char buffer[64])
double formated_size = 0.0;
format_memory_size(prefix_size, size, &formated_size);
// Generate human-readable used size
// Generate human-readable "total used" size
char prefix_used[2] = { 0 };
double formated_used = 0.0;
format_memory_size(prefix_used, used, &formated_used);
// Generate human-readable "used by FTL" size
char prefix_FTL[2] = { 0 };
double formated_FTL = 0.0;
format_memory_size(prefix_FTL, used_shmem, &formated_FTL);
// Print result into buffer passed to this subroutine
snprintf(buffer, 64, SHMEM_PATH": %.1f%sB used, %.1f%sB total", formated_used, prefix_used, formated_size, prefix_size);
snprintf(buffer, 64, SHMEM_PATH": %.1f%sB used, %.1f%sB total, FTL uses %.1f%sB",
formated_used, prefix_used, formated_size, prefix_size, formated_FTL, prefix_FTL);
// Return percentage of used shared memory
// Adding 1 avoids FPE if the size turns out to be zero
@ -595,28 +602,14 @@ SharedMemory create_shm(const char *name, const size_t size, bool create_new)
};
// O_RDWR: Open the object for read-write access (we need to be able to modify the locks)
int shm_oflags = O_RDWR;
if(create_new)
{
// Try unlinking the shared memory object before creating a new one.
// If the object is still existing, e.g., due to a past unclean exit
// of FTL, shm_open() would fail with error "File exists"
int ret = shm_unlink(name);
// Check return code. shm_unlink() returns -1 on error and sets errno
// We specifically ignore ENOENT (No such file or directory) as this is not an
// error in our use case (we only want the file to be deleted when existing)
if(ret != 0 && errno != ENOENT)
logg("create_shm(): shm_unlink(\"%s\") failed: %s (%i)", name, strerror(errno), errno);
// Replace shm_oflags
// O_CREAT: Create the shared memory object if it does not exist.
// O_EXCL: Return an error if a shared memory object with the given name already exists.
// O_TRUNC: If the shared memory object already exists, truncate it to zero bytes.
shm_oflags |= O_CREAT | O_EXCL | O_TRUNC;
}
// When creating a new shared memory object, we add to this
// - O_CREAT: Create the shared memory object if it does not exist.
// - O_EXCL: Return an error if a shared memory object with the given name already exists.
const int shm_oflags = create_new ? O_RDWR | O_CREAT | O_EXCL : O_RDWR;
// Create the shared memory file in read/write mode with 600 permissions
int fd = shm_open(sharedMemory.name, shm_oflags, S_IRUSR | S_IWUSR);
errno = 0;
const int fd = shm_open(sharedMemory.name, shm_oflags, S_IRUSR | S_IWUSR);
// Check for `shm_open` error
if(fd == -1)
@ -638,6 +631,10 @@ SharedMemory create_shm(const char *name, const size_t size, bool create_new)
exit(EXIT_FAILURE);
}
// Update how much memory FTL uses
// We only add here as this is a new file
used_shmem += size;
// Create shared memory mapping
void *shm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
@ -783,6 +780,10 @@ bool realloc_shm(SharedMemory *sharedMemory, const size_t size1, const size_t si
exit(EXIT_FAILURE);
}
// Update how much memory FTL uses
// We add the difference between updated and previous size
used_shmem += (size - sharedMemory->size);
sharedMemory->ptr = new_ptr;
sharedMemory->size = size;

View File

@ -1,5 +1,12 @@
#!./test/libs/bats/bin/bats
@test "Running a second instance is detected and prevented" {
run bash -c 'su pihole -s /bin/sh -c "/home/pihole/pihole-FTL -f"'
printf "%s\n" "${lines[@]}"
[[ ${lines[9]} == *"Initialization of shared memory failed." ]]
[[ ${lines[10]} == *"--> pihole-FTL is already running as PID "* ]]
}
@test "Starting tests without prior history" {
run bash -c 'grep -c "Total DNS queries: 0" /var/log/pihole-FTL.log'
printf "%s\n" "${lines[@]}"
@ -297,8 +304,8 @@
[[ ${lines[0]} == "0" ]]
}
@test "No FATAL messages in pihole-FTL.log" {
run bash -c 'grep -c "FATAL:" /var/log/pihole-FTL.log'
@test "No FATAL messages in pihole-FTL.log (besides error due to starting FTL more than once)" {
run bash -c 'grep "FATAL:" /var/log/pihole-FTL.log | grep -c -v "FATAL: create_shm(): Failed to create shared memory object \"FTL-lock\": File exists"'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "0" ]]
}