Merge branch 'development' into new/super-clients

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2020-11-15 08:47:44 +01:00
commit 6f6ae518d2
No known key found for this signature in database
GPG Key ID: 9A379E34963EE435
35 changed files with 701 additions and 454 deletions

View File

@ -3,52 +3,76 @@ version: 2
.job_steps: &job_steps
steps:
- checkout
- run:
name: "Setup"
command: |
if [[ $CIRCLE_JOB == *"qemu"* ]] ; then sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset ; fi
- run:
name: "Build"
no_output_timeout: 30m
command: |
BRANCH=$([ -z "$CIRCLE_TAG" ] && echo "$CIRCLE_BRANCH" || echo "master")
[[ $CIRCLE_JOB == *"qemu"* ]] && DOCKERIFNEEDED="docker run --rm -v $(pwd):/workspace -w /workspace pihole/ftl-build:arm-qemu "
$DOCKERIFNEEDED bash .circleci/build-CI.sh "${STATIC}" "${BRANCH}" "${CIRCLE_TAG}" "${CIRCLE_JOB}"
bash .circleci/build-CI.sh "${STATIC}" "${BRANCH}" "${CIRCLE_TAG}" "${CIRCLE_JOB}"
- run:
name: "Binary checks"
command: bash test/arch_test.sh
- run:
name: "Upload"
name: "Compute checksum"
command: |
mv pihole-FTL "${BIN_NAME}"
sha1sum pihole-FTL-* > ${BIN_NAME}.sha1
- run:
name: "Upload binary to binary bucket"
command: |
[ -z "${CIRCLE_PR_USERNAME}" ] || exit 0
DIR="${CIRCLE_TAG:-${CIRCLE_BRANCH}}"
mv pihole-FTL "${BIN_NAME}"
sha1sum pihole-FTL-* > ${BIN_NAME}.sha1
mkdir -p ~/.ssh/
ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
sftp -b - $SSH_USER@$SSH_HOST <<< "-mkdir ${DIR}
put ${BIN_NAME}* ${DIR}"
mv "${BIN_NAME}" pihole-FTL
sftp -b - $SSH_USER@$SSH_HOST <<< "-mkdir html/${DIR}
put ${BIN_NAME}* html/${DIR}"
- run:
name: "Verify uploaded binary"
command: |
[ -z "${CIRCLE_PR_USERNAME}" ] || exit 0
DIR="${CIRCLE_TAG:-${CIRCLE_BRANCH}}"
mkdir download
cd download
wget "https://ftl.pi-hole.net/${DIR}/${BIN_NAME}"
wget "https://ftl.pi-hole.net/${DIR}/${BIN_NAME}.sha1"
sha1sum -c "${BIN_NAME}.sha1"
cd ..
- run:
name: "Test"
command: |
mv "${BIN_NAME}" pihole-FTL
test/run.sh
.docker_template: &docker_template
docker:
- image: pihole/ftl-build:v1.3-$CIRCLE_JOB
- image: pihole/ftl-build:v1.7-$CIRCLE_JOB
<<: *job_steps
jobs:
arm:
armv4t:
<<: *docker_template
environment:
BIN_NAME: "pihole-FTL-arm-linux-gnueabi"
BIN_NAME: "pihole-FTL-armv4-linux-gnueabi"
armhf:
armv5te:
<<: *docker_template
environment:
BIN_NAME: "pihole-FTL-arm-linux-gnueabihf"
BIN_NAME: "pihole-FTL-armv5-linux-gnueabi"
armv6hf:
<<: *docker_template
environment:
BIN_NAME: "pihole-FTL-armv6-linux-gnueabihf"
armv7hf:
<<: *docker_template
environment:
BIN_NAME: "pihole-FTL-armv7-linux-gnueabihf"
armv8a:
<<: *docker_template
environment:
BIN_NAME: "pihole-FTL-armv8-linux-gnueabihf"
aarch64:
<<: *docker_template
@ -70,26 +94,27 @@ jobs:
environment:
BIN_NAME: "pihole-FTL-linux-x86_32"
arm-qemu:
machine:
enabled: true
environment:
BIN_NAME: "pihole-FTL-armel-native"
<<: *job_steps
workflows:
version: 2
build:
jobs:
- arm:
- armv4t:
filters:
tags:
only: /^v.*/
- arm-qemu:
- armv5te:
filters:
tags:
only: /^v.*/
- armhf:
- armv6hf:
filters:
tags:
only: /^v.*/
- armv7hf:
filters:
tags:
only: /^v.*/
- armv8a:
filters:
tags:
only: /^v.*/

View File

@ -199,7 +199,7 @@ find_program(SETCAP setcap)
install(TARGETS pihole-FTL
RUNTIME DESTINATION bin
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(CODE "execute_process(COMMAND ${SETCAP} CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/pihole-FTL)")
install(CODE "execute_process(COMMAND ${SETCAP} CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE+eip \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/pihole-FTL)")
add_subdirectory(api)
add_subdirectory(database)

View File

@ -125,6 +125,10 @@
#define calloc(numer_of_elements, element_size) FTLcalloc(numer_of_elements, element_size, __FILE__, __FUNCTION__, __LINE__)
#define realloc(ptr, new_size) FTLrealloc(ptr, new_size, __FILE__, __FUNCTION__, __LINE__)
// Preprocessor help functions
#define str(x) # x
#define xstr(x) str(x)
extern pthread_t telnet_listenthreadv4;
extern pthread_t telnet_listenthreadv6;
extern pthread_t socket_listenthread;

View File

@ -525,6 +525,7 @@ void getUpstreamDestinations(const char *client_message, const int *sock)
{
float percentage = 0.0f;
const char* ip, *name;
in_port_t upstream_port = 0;
if(i == -2)
{
@ -548,7 +549,7 @@ void getUpstreamDestinations(const char *client_message, const int *sock)
}
else
{
// Regular forward destionation
// Regular upstream destination
// Get sorted indices
int upstreamID;
if(sort)
@ -556,18 +557,22 @@ void getUpstreamDestinations(const char *client_message, const int *sock)
else
upstreamID = i;
// Get forward pointer
const upstreamsData* forward = getUpstream(upstreamID, true);
if(forward == NULL)
// Get upstream pointer
const upstreamsData* upstream = getUpstream(upstreamID, true);
if(upstream == NULL)
continue;
// Get IP and host name of forward destination if available
ip = getstr(forward->ippos);
name = getstr(forward->namepos);
// Get IP and host name of upstream destination if available
ip = getstr(upstream->ippos);
if(upstream->namepos != 0)
name = getstr(upstream->namepos);
else
name = getstr(upstream->ippos);
upstream_port = upstream->port;
// Get percentage
if(totalqueries > 0)
percentage = 1e2f * forward->count / totalqueries;
percentage = 1e2f * upstream->count / totalqueries;
}
// Send data:
@ -576,7 +581,11 @@ void getUpstreamDestinations(const char *client_message, const int *sock)
if(percentage > 0.0f || i < 0)
{
if(istelnet[*sock])
ssend(*sock, "%i %.2f %s %s\n", i, percentage, ip, name);
if(upstream_port != 0)
ssend(*sock, "%i %.2f %s#%u %s#%u\n", i, percentage,
ip, upstream_port, name, upstream_port);
else
ssend(*sock, "%i %.2f %s %s\n", i, percentage, ip, name);
else
{
if(!pack_str32(*sock, name) || !pack_str32(*sock, ip))
@ -706,6 +715,15 @@ void getAllQueries(const char *client_message, const int *sock)
forwarddestid = -2;
else
{
// Extract address/name and port
char serv_addr[INET6_ADDRSTRLEN] = { 0 };
unsigned int serv_port = 53;
// 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(forwarddest, "%"xstr(INET6_ADDRSTRLEN)"[^#]#%u", serv_addr, &serv_port);
serv_addr[INET6_ADDRSTRLEN-1] = '\0';
// Iterate through all known forward destinations
forwarddestid = -3;
for(int i = 0; i < counters->upstreams; i++)
@ -717,9 +735,9 @@ void getAllQueries(const char *client_message, const int *sock)
// Try to match the requested string against their IP addresses and
// (if available) their host names
if(strcmp(getstr(forward->ippos), forwarddest) == 0 ||
if((strcmp(getstr(forward->ippos), serv_addr) == 0 ||
(forward->namepos != 0 &&
strcmp(getstr(forward->namepos), forwarddest) == 0))
strcmp(getstr(forward->namepos), serv_addr) == 0)) && forward->port == serv_port)
{
forwarddestid = i;
break;
@ -979,9 +997,28 @@ void getAllQueries(const char *client_message, const int *sock)
regex_idx = dns_cache->black_regex_idx;
}
// Get IP of upstream destination, if applicable
in_port_t upstream_port = 0;
const char *upstream_name = "N/A";
if(query->status == QUERY_FORWARDED)
{
const upstreamsData *upstream = getUpstream(query->upstreamID, true);
if(upstream != NULL)
{
if(upstream->namepos != 0)
// Get upstream destination name if possible
upstream_name = getstr(upstream->namepos);
else
// If we have no name, get the IP address
upstream_name = getstr(upstream->ippos);
upstream_port = upstream->port;
}
}
if(istelnet[*sock])
{
ssend(*sock,"%lli %s %s %s %i %i %i %lu %s %i",
ssend(*sock,"%lli %s %s %s %i %i %i %lu %s %i %s",
(long long)query->timestamp,
qtype,
domain,
@ -991,7 +1028,11 @@ void getAllQueries(const char *client_message, const int *sock)
query->reply,
delay,
CNAME_domain,
regex_idx);
regex_idx,
upstream_name);
if(upstream_port != 0)
ssend(*sock, "#%u", upstream_port);
if(config.debug & DEBUG_API)
ssend(*sock, " %i", queryID);
ssend(*sock, "\n");

View File

@ -720,6 +720,10 @@ void read_debuging_settings(FILE *fp)
// defaults to: false
setDebugOption(fp, "DEBUG_EVENTS", DEBUG_EVENTS);
// DEBUG_HELPER
// defaults to: false
setDebugOption(fp, "DEBUG_HELPER", DEBUG_HELPER);
if(config.debug)
{
logg("*****************************");
@ -744,6 +748,7 @@ void read_debuging_settings(FILE *fp)
logg("* DEBUG_CLIENTS %s *", (config.debug & DEBUG_CLIENTS)? "YES":"NO ");
logg("* DEBUG_SUPERCLIENTS %s *", (config.debug & DEBUG_SUPERCLIENTS)? "YES":"NO ");
logg("* DEBUG_EVENTS %s *", (config.debug & DEBUG_EVENTS)? "YES":"NO ");
logg("* DEBUG_HELPER %s *", (config.debug & DEBUG_HELPER)? "YES":"NO ");
logg("*****************************");
}

View File

@ -41,6 +41,7 @@ typedef struct {
bool ignore_localhost;
bool analyze_only_A_AAAA;
bool DBimport;
bool DBexport;
bool parse_arp_cache;
bool cname_inspection;
bool block_esni;

View File

@ -26,7 +26,6 @@
#include "superclients.h"
sqlite3 *FTL_db = NULL;
bool database = true;
bool DBdeleteoldqueries = false;
long int lastdbindex = 0;
static bool db_avail = false;
@ -40,31 +39,29 @@ __attribute__ ((pure)) bool FTL_DB_avail(void)
void dbclose(void)
{
// Mark database as being closed
db_avail = false;
if(config.debug & DEBUG_LOCKS)
logg("Unlocking FTL database");
// Only try to close an existing database connection
int rc = SQLITE_OK;
if( FTL_db != NULL )
{
rc = sqlite3_close(FTL_db);
if((rc = sqlite3_close(FTL_db)) != SQLITE_OK)
logg("Encountered error while trying to close database: %s", sqlite3_errstr(rc));
FTL_db = NULL;
}
db_avail = false;
// Report any error
if( rc != SQLITE_OK )
{
logg("Encountered error while trying to close database: %s", sqlite3_errstr(rc));
database = false;
}
if(config.debug & DEBUG_LOCKS)
logg("Unlocking database");
else if(config.debug & DEBUG_LOCKS)
logg("Unlocking FTL database: already NULL");
// Unlock mutex on the database
pthread_mutex_unlock(&dblock);
if(config.debug & DEBUG_LOCKS)
logg("Unlocking database: Success");
logg("Unlocking FTL database: Success");
}
bool dbopen(void)
@ -73,25 +70,28 @@ bool dbopen(void)
if(FTL_db != NULL && db_avail)
{
if(config.debug & DEBUG_LOCKS)
logg("Not locking database (already open)");
logg("Not locking FTL database (already open)");
return true;
}
// Do not open database if it is not to be used
if(!use_database())
return false;
if(config.debug & DEBUG_LOCKS)
logg("Locking database");
logg("Locking FTL database");
// Lock mutex on the database
pthread_mutex_lock(&dblock);
if(config.debug & DEBUG_LOCKS)
logg("Locking database: Success");
logg("Locking FTL database: Success");
// Try to open database
int rc = sqlite3_open_v2(FTLfiles.FTL_db, &FTL_db, SQLITE_OPEN_READWRITE, NULL);
if( rc != SQLITE_OK )
{
logg("Encountered error while trying to open database: %s", sqlite3_errstr(rc));
database = false;
pthread_mutex_unlock(&dblock);
return false;
}
@ -102,7 +102,6 @@ bool dbopen(void)
{
logg("Encountered error while trying to set busy timeout (%d ms) on database: %s",
DATABASE_BUSY_TIMEOUT, sqlite3_errstr(rc));
database = false;
dbclose();
return false;
}
@ -112,6 +111,13 @@ bool dbopen(void)
return true;
}
// (Re-)Open pihole-FTL database connection
void piholeFTLDB_reopen(void)
{
dbclose();
dbopen();
}
int dbquery(const char *format, ...)
{
va_list args;
@ -127,7 +133,7 @@ int dbquery(const char *format, ...)
// Log generated SQL string when dbquery() is called
// although the database connection is not available
if(FTL_db == NULL && !dbopen())
if(!FTL_DB_avail())
{
logg("dbquery(\"%s\") called but database is not available!", query);
sqlite3_free(query);
@ -166,19 +172,19 @@ static bool create_counter_table(void)
// ID 0 = total queries
if(!db_set_counter(DB_TOTALQUERIES, 0))
{ dbclose(); return false; }
return false;
// ID 1 = total blocked queries
if(!db_set_counter(DB_BLOCKEDQUERIES, 0))
{ dbclose(); return false; }
return false;
// Time stamp of creation of the counters database
if(!db_set_FTL_property(DB_FIRSTCOUNTERTIMESTAMP, time(NULL)))
{ dbclose(); return false; }
return false;
// Update database version to 2
if(!db_set_FTL_property(DB_VERSION, 2))
{ dbclose(); return false; }
return false;
return true;
}
@ -191,6 +197,10 @@ static bool db_create(void)
logg("Encountered error while trying to create database in rw-mode: %s", sqlite3_errstr(rc));
return false;
}
// Mark database as being available so dbquery() doesn't error out
db_avail = true;
// Create Queries table in the database
SQL_bool("CREATE TABLE queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain TEXT NOT NULL, client TEXT NOT NULL, forward TEXT );");
@ -210,7 +220,7 @@ static bool db_create(void)
return false;
// Done initializing the database
// Close database handle
// Close database handle, it will be reopened in db_init()
dbclose();
// Explicitly set permissions to 0644
@ -240,9 +250,6 @@ void db_init(void)
exit(EXIT_FAILURE);
}
// Lock database thread
pthread_mutex_lock(&dblock);
// Initialize SQLite3 logging callback
// This ensures SQLite3 errors and warnings are logged to pihole-FTL.log
// We use this to possibly catch even more errors in places we do not
@ -260,18 +267,12 @@ void db_init(void)
{
logg("Creation of database failed, database is not available");
pthread_mutex_unlock(&dblock);
database = false;
return;
}
}
// Try to open the database connection
rc = sqlite3_open_v2(FTLfiles.FTL_db, &FTL_db, SQLITE_OPEN_READWRITE, NULL);
if( rc != SQLITE_OK ){
logg("Cannot initialize (open) long-term database: %s", sqlite3_errstr(rc));
database = false;
return;
}
// Open database
dbopen();
db_avail = true;
@ -281,8 +282,6 @@ void db_init(void)
{
logg("Database not available, please ensure the database is unlocked when starting pihole-FTL !");
dbclose();
database = false;
return;
}
else
@ -300,8 +299,6 @@ void db_init(void)
{
logg("Counter table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -317,8 +314,6 @@ void db_init(void)
{
logg("Network table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -334,8 +329,6 @@ void db_init(void)
{
logg("Unable to unify clients in network table, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -351,8 +344,6 @@ void db_init(void)
{
logg("Network-addresses table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -368,8 +359,6 @@ void db_init(void)
{
logg("Message table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -386,8 +375,6 @@ void db_init(void)
{
logg("Column additional_info not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -403,8 +390,6 @@ void db_init(void)
{
logg("Network addresses table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -420,8 +405,6 @@ void db_init(void)
{
logg("Superclients table not initialized, database not available");
dbclose();
database = false;
return;
}
// Get updated version
@ -439,16 +422,20 @@ void db_init(void)
if(!use_database())
{
logg("Not using the long-term database for storing queries");
database = false;
config.DBexport = false;
return;
}
config.DBexport = true;
// Close database here, we have to reopen it later (after forking)
dbclose();
logg("Database successfully initialized");
}
int db_get_FTL_property(const enum ftl_table_props ID)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("db_get_FTL_property(%u) called but database is not available!", ID);
return DB_FAILED;
@ -471,7 +458,7 @@ int db_get_FTL_property(const enum ftl_table_props ID)
bool db_set_FTL_property(const enum ftl_table_props ID, const int value)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("db_set_FTL_property(%u, %i) called but database is not available!", ID, value);
return false;
@ -481,31 +468,48 @@ bool db_set_FTL_property(const enum ftl_table_props ID, const int value)
bool db_set_counter(const enum counters_table_props ID, const int value)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("db_set_counter(%u, %i) called but database is not available!", ID, value);
return false;
}
return dbquery("INSERT OR REPLACE INTO counters (id, value) VALUES ( %u, %i );", ID, value) == SQLITE_OK;
if(dbquery("INSERT OR REPLACE INTO counters (id, value) VALUES ( %u, %i );", ID, value) != SQLITE_OK)
{
dbclose();
return false;
}
return true;
}
bool db_update_counters(const int total, const int blocked)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("db_update_counters(%i, %i) called but database is not available!", total, blocked);
dbclose();
return false;
}
if(dbquery("UPDATE counters SET value = value + %i WHERE id = %i;", total, DB_TOTALQUERIES) != SQLITE_OK)
{
dbclose();
return false;
}
if(dbquery("UPDATE counters SET value = value + %i WHERE id = %i;", blocked, DB_BLOCKEDQUERIES) != SQLITE_OK)
{
dbclose();
return false;
}
return true;
}
int db_query_int(const char* querystr)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("db_query_int(\"%s\") called but database is not available!", querystr);
return DB_FAILED;
@ -521,10 +525,8 @@ int db_query_int(const char* querystr)
if( rc != SQLITE_OK )
{
if( rc != SQLITE_BUSY )
{
logg("Encountered prepare error in db_query_int(\"%s\"): %s", querystr, sqlite3_errstr(rc));
database = false;
}
return DB_FAILED;
}
@ -562,7 +564,7 @@ int db_query_int(const char* querystr)
long int get_max_query_ID(void)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("get_max_query_ID() called but database is not available!");
return DB_FAILED;
@ -581,9 +583,10 @@ long int get_max_query_ID(void)
if( rc != SQLITE_BUSY )
{
logg("Encountered prepare error in get_max_query_ID(): %s", sqlite3_errstr(rc));
database = false;
dbclose();
}
dbclose();
// Return okay if the database is busy
return DB_FAILED;
}
@ -591,7 +594,6 @@ long int get_max_query_ID(void)
if( rc != SQLITE_ROW )
{
logg("Encountered step error in get_max_query_ID(): %s", sqlite3_errstr(rc));
database = false;
dbclose();
return DB_FAILED;
}
@ -608,7 +610,7 @@ long int get_max_query_ID(void)
// Returns ID of the most recent successful INSERT.
long get_lastID(void)
{
if(!database || FTL_db == NULL)
if(!FTL_DB_avail())
{
logg("get_lastID() called but database is not available!");
return DB_FAILED;

View File

@ -35,6 +35,7 @@ int dbquery(const char *format, ...);
bool FTL_DB_avail(void) __attribute__ ((pure));
bool dbopen(void);
void dbclose(void);
void piholeFTLDB_reopen(void);
int db_query_int(const char*);
long get_lastID(void);
void SQLite3LogCallback(void *pArg, int iErrCode, const char *zMsg);
@ -45,7 +46,6 @@ const char *get_sqlite3_version(void);
bool use_database(void) __attribute__ ((pure));
extern sqlite3 *FTL_db;
extern bool database;
extern long int lastdbindex;
extern bool DBdeleteoldqueries;

View File

@ -44,7 +44,7 @@ void *DB_thread(void *val)
while(!killed)
{
if(database)
if(FTL_DB_avail())
{
time_t now = time(NULL);
if(now - lastDBsave >= config.DBinterval)
@ -52,15 +52,13 @@ void *DB_thread(void *val)
// Update lastDBsave timer
lastDBsave = time(NULL) - time(NULL)%config.DBinterval;
// Lock FTL's data structures, since it is
// likely that they will be changed here
lock_shm();
// Save data to database
DB_save_queries();
// Release data lock
unlock_shm();
if(config.DBexport)
{
lock_shm();
DB_save_queries();
unlock_shm();
}
// Check if GC should be done on the database
if(DBdeleteoldqueries)
@ -79,6 +77,9 @@ void *DB_thread(void *val)
// database is not updated very often)
if(now % 2592000L == 0)
updateMACVendorRecords();
if(get_and_clear_event(PARSE_NEIGHBOR_CACHE))
parse_neighbor_cache();
}
// Process database related event queue elements
@ -88,8 +89,12 @@ void *DB_thread(void *val)
if(get_and_clear_event(RELOAD_PRIVACY_LEVEL))
get_privacy_level(NULL);
if(get_and_clear_event(PARSE_NEIGHBOR_CACHE))
parse_neighbor_cache();
if(get_and_clear_event(RERESOLVE_DATABASE_NAMES))
{
// Try to resolve host names from clients in the network table
// which have empty/undefined host names
resolveNetworkTableNames();
}
if(get_and_clear_event(REIMPORT_SUPERCLIENTS))
{

View File

@ -72,28 +72,18 @@ bool create_message_table(void)
// Flush message table
bool flush_message_table(void)
{
// Open database connection
dbopen();
// Flush message table
SQL_bool("DELETE FROM message;");
// Close database connection
dbclose();
return true;
}
static bool add_message(enum message_type type, const char *message,
const int count,...)
{
bool opened_database = false;
// Open database connection (if not already open)
if(!FTL_DB_avail())
{
if(!dbopen())
return false;
opened_database = true;
return false;
}
// Ensure there are no duplicates when adding host name messages
@ -105,8 +95,7 @@ static bool add_message(enum message_type type, const char *message,
if( rc != SQLITE_OK ){
logg("add_message(type=%u, message=%s) - SQL error prepare DELETE: %s",
type, message, sqlite3_errstr(rc));
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -117,8 +106,7 @@ static bool add_message(enum message_type type, const char *message,
type, message, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -129,8 +117,7 @@ static bool add_message(enum message_type type, const char *message,
type, message, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -139,8 +126,7 @@ static bool add_message(enum message_type type, const char *message,
{
logg("add_message(type=%u, message=%s) - SQL error step DELETE: %s",
type, message, sqlite3_errstr(rc));
if(opened_database)
dbclose();
dbclose();
return false;
}
sqlite3_clear_bindings(stmt);
@ -157,8 +143,7 @@ static bool add_message(enum message_type type, const char *message,
{
logg("add_message(type=%u, message=%s) - SQL error prepare: %s",
type, message, sqlite3_errstr(rc));
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -169,8 +154,7 @@ static bool add_message(enum message_type type, const char *message,
type, message, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -181,8 +165,7 @@ static bool add_message(enum message_type type, const char *message,
type, message, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(opened_database)
dbclose();
dbclose();
return false;
}
@ -214,8 +197,7 @@ static bool add_message(enum message_type type, const char *message,
type, message, 3 + j, datatype, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(opened_database)
dbclose();
dbclose();
return false;
}
}
@ -230,15 +212,10 @@ static bool add_message(enum message_type type, const char *message,
if(rc != SQLITE_DONE)
{
logg("Encountered error while trying to store message in long-term database: %s", sqlite3_errstr(rc));
if(opened_database)
dbclose();
dbclose();
return false;
}
// Close database connection (if we opened it)
if(opened_database)
dbclose();
return true;
}

View File

@ -842,19 +842,15 @@ static bool add_FTL_clients_to_network_table(enum arp_status *client_status, tim
{
const char *text;
if( rc == SQLITE_BUSY )
{
text = "WARNING";
}
else
{
text = "ERROR";
// We shall not use the database any longer
database = false;
dbclose();
}
logg("%s: Storing devices in network table failed: %s", text, sqlite3_errstr(rc));
unlock_shm();
dbclose();
return false;
}
@ -1054,9 +1050,9 @@ static bool add_local_interfaces_to_network_table(time_t now, unsigned int *addi
void parse_neighbor_cache(void)
{
// Open database file
if(!dbopen())
if(!FTL_DB_avail())
{
logg("parse_neighbor_cache() - Failed to open DB");
logg("parse_neighbor_cache() - Database is not available");
return;
}
@ -1088,19 +1084,15 @@ void parse_neighbor_cache(void)
{
const char *text;
if( rc == SQLITE_BUSY )
{
text = "WARNING";
}
else
{
text = "ERROR";
// We shall not use the database any longer
database = false;
dbclose();
}
// dbquery() above already logs the reson for why the query failed
logg("%s: Storing devices in network table (\"%s\") failed", text, sql);
dbclose();
return;
}
@ -1331,26 +1323,20 @@ void parse_neighbor_cache(void)
if((rc = dbquery("END TRANSACTION")) != SQLITE_OK) {
const char *text;
if( rc == SQLITE_BUSY )
{
text = "WARNING";
}
else
{
text = "ERROR";
// We shall not use the database any longer
database = false;
dbclose();
}
logg("%s: Storing devices in network table failed: %s", text, sqlite3_errstr(rc));
unlock_shm();
dbclose();
// Return okay if the database is busy
return;
}
// Close database connection
// We opened the connection in this function
dbclose();
unlock_shm();
// Debug logging
@ -1483,7 +1469,7 @@ static char *getMACVendor(const char *hwaddr)
hwaddr, hwaddrshort, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
dbclose();
sqlite3_close(macvendor_db);
return strdup("");
}
@ -1525,11 +1511,9 @@ void updateMACVendorRecords(void)
return;
}
// Open pihole-FTL.db database file if needed
const bool db_already_open = FTL_DB_avail();
if(!db_already_open && !dbopen())
if(!FTL_DB_avail())
{
logg("updateMACVendorRecords() - Failed to open DB");
logg("updateMACVendorRecords() - Database not available");
return;
}
@ -1538,8 +1522,6 @@ void updateMACVendorRecords(void)
int rc = sqlite3_prepare_v2(FTL_db, selectstr, -1, &stmt, NULL);
if( rc != SQLITE_OK ){
logg("updateMACVendorRecords() - SQL error prepare \"%s\": %s", selectstr, sqlite3_errstr(rc));
if(!db_already_open)
dbclose();
return;
}
@ -1584,18 +1566,14 @@ void updateMACVendorRecords(void)
}
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
}
// Get hardware address of device identified by IP address
char *__attribute__((malloc)) getMACfromIP(const char *ipaddr)
{
// Open pihole-FTL.db database file if needed
const bool db_already_open = FTL_DB_avail();
if(!db_already_open && !dbopen())
if(!FTL_DB_avail())
{
logg("getMACfromIP(\"%s\") - Failed to open DB", ipaddr);
logg("getMACfromIP(\"%s\") - Database not available", ipaddr);
return NULL;
}
@ -1610,8 +1588,6 @@ char *__attribute__((malloc)) getMACfromIP(const char *ipaddr)
if( rc != SQLITE_OK ){
logg("getMACfromIP(\"%s\") - SQL error prepare: %s",
ipaddr, sqlite3_errstr(rc));
if(!db_already_open)
dbclose();
return NULL;
}
@ -1622,8 +1598,6 @@ char *__attribute__((malloc)) getMACfromIP(const char *ipaddr)
ipaddr, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return NULL;
}
@ -1646,8 +1620,6 @@ char *__attribute__((malloc)) getMACfromIP(const char *ipaddr)
// Finalize statement and close database handle
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return hwaddr;
}
@ -1717,11 +1689,9 @@ int getSuperclientIDfromIP(const char *ipaddr)
// Get host name of device identified by IP address
char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
{
// Open pihole-FTL.db database file if needed
const bool db_already_open = FTL_DB_avail();
if(!db_already_open && !dbopen())
if(!FTL_DB_avail())
{
logg("getNameFromIP(\"%s\") - Failed to open DB", ipaddr);
logg("getNameFromIP(\"%s\") - Database not available", ipaddr);
return NULL;
}
@ -1732,8 +1702,6 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
if( rc != SQLITE_OK ){
logg("getNameFromIP(\"%s\") - SQL error prepare: %s",
ipaddr, sqlite3_errstr(rc));
if(!db_already_open)
dbclose();
return NULL;
}
@ -1744,8 +1712,6 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
ipaddr, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return NULL;
}
@ -1770,12 +1736,7 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
// Return here if we found the name
if(name != NULL)
{
if(!db_already_open)
dbclose();
return name;
}
// Nothing found for the exact IP address
// Check for a host name associated with the same device (but another IP address)
@ -1788,8 +1749,6 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
if( rc != SQLITE_OK ){
logg("getNameFromIP(\"%s\") - SQL error prepare: %s",
ipaddr, sqlite3_errstr(rc));
if(!db_already_open)
dbclose();
return NULL;
}
@ -1800,8 +1759,6 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
ipaddr, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return NULL;
}
@ -1825,8 +1782,6 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
// Finalize statement and close database handle
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return name;
}
@ -1834,11 +1789,9 @@ char *__attribute__((malloc)) getNameFromIP(const char *ipaddr)
// Get interface of device identified by IP address
char *__attribute__((malloc)) getIfaceFromIP(const char *ipaddr)
{
// Open pihole-FTL.db database file if needed
const bool db_already_open = FTL_DB_avail();
if(!db_already_open && !dbopen())
if(!FTL_DB_avail())
{
logg("getIfaceFromIP(\"%s\") - Failed to open DB", ipaddr);
logg("getIfaceFromIP(\"%s\") - Database not available", ipaddr);
return NULL;
}
@ -1854,8 +1807,6 @@ char *__attribute__((malloc)) getIfaceFromIP(const char *ipaddr)
if( rc != SQLITE_OK ){
logg("getIfaceFromIP(\"%s\") - SQL error prepare: %s",
ipaddr, sqlite3_errstr(rc));
if(!db_already_open)
dbclose();
return NULL;
}
@ -1872,8 +1823,6 @@ char *__attribute__((malloc)) getIfaceFromIP(const char *ipaddr)
ipaddr, sqlite3_errstr(rc));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return NULL;
}
@ -1896,8 +1845,6 @@ char *__attribute__((malloc)) getIfaceFromIP(const char *ipaddr)
// Finalize statement and close database handle
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
if(!db_already_open)
dbclose();
return iface;
}
@ -1905,11 +1852,9 @@ char *__attribute__((malloc)) getIfaceFromIP(const char *ipaddr)
// Resolve unknown names of recently seen IP addresses in network table
void resolveNetworkTableNames(void)
{
// Open pihole-FTL.db database file if needed
const bool db_already_open = FTL_DB_avail();
if(!db_already_open && !dbopen())
if(!FTL_DB_avail())
{
logg("resolveNetworkTableNames() - Failed to open DB");
logg("resolveNetworkTableNames() - Database not available");
return;
}
@ -1929,8 +1874,7 @@ void resolveNetworkTableNames(void)
// dbquery() above already logs the reson for why the query failed
logg("%s: Trying to resolve unknown network table host names (\"%s\") failed", text, sql);
if(!db_already_open)
dbclose();
return;
}
@ -1946,12 +1890,9 @@ void resolveNetworkTableNames(void)
logg("resolveNetworkTableNames() - SQL error prepare: %s",
sqlite3_errstr(rc));
sqlite3_finalize(table_stmt);
if(!db_already_open)
dbclose();
return;
}
// Get data
while((rc = sqlite3_step(table_stmt)) == SQLITE_ROW)
{
@ -2027,8 +1968,6 @@ void resolveNetworkTableNames(void)
logg("resolveNetworkTableNames() - SQL error step: %s",
sqlite3_errstr(rc));
sqlite3_finalize(table_stmt);
if(!db_already_open)
dbclose();
return;
}
@ -2036,6 +1975,4 @@ void resolveNetworkTableNames(void)
sqlite3_finalize(table_stmt);
dbquery("COMMIT");
if(!db_already_open)
dbclose();
}

View File

@ -25,14 +25,15 @@
#include "../config.h"
// getstr()
#include "../shmem.h"
// free()
#include "../memory.h"
static bool saving_failed_before = false;
int get_number_of_queries_in_DB(void)
{
// This routine is used by the API routines.
// We need to handle opening/closing of the database herein.
if(!dbopen())
if(!FTL_DB_avail())
{
return DB_FAILED;
}
@ -40,9 +41,6 @@ int get_number_of_queries_in_DB(void)
// Count number of rows using the index timestamp is faster than select(*)
int result = db_query_int("SELECT COUNT(timestamp) FROM queries");
// Close pihole-FTL.db database connection
dbclose();
return result;
}
@ -52,13 +50,6 @@ void DB_save_queries(void)
if(config.debug & DEBUG_DATABASE)
timer_start(DATABASE_WRITE_TIMER);
// Open database
if(!dbopen())
{
logg("Failed to open long-term database when trying to store queries");
return;
}
unsigned int saved = 0;
bool error = false;
sqlite3_stmt* stmt = NULL;
@ -68,18 +59,14 @@ void DB_save_queries(void)
{
const char *text;
if( rc == SQLITE_BUSY )
{
text = "WARNING";
}
else
{
text = "ERROR";
// We shall not use the database any longer
database = false;
dbclose();
}
logg("%s: Storing queries in long-term database failed: %s", text, sqlite3_errstr(rc));
dbclose();
return;
}
@ -96,15 +83,13 @@ void DB_save_queries(void)
{
text = "ERROR";
spaces = " ";
// We shall not use the database any longer
database = false;
dbclose();
}
// dbquery() above already logs the reson for why the query failed
logg("%s: Storing queries in long-term database failed: %s\n", text, sqlite3_errstr(rc));
logg("%s Keeping queries in memory for later new attempt", spaces);
saving_failed_before = true;
dbclose();
return;
}
@ -160,7 +145,14 @@ void DB_save_queries(void)
{
// Get forward pointer
const upstreamsData* upstream = getUpstream(query->upstreamID, true);
sqlite3_bind_text(stmt, 6, getstr(upstream->ippos), -1, SQLITE_STATIC);
char *buffer = NULL;
if(asprintf(&buffer, "%s#%u", getstr(upstream->ippos), upstream->port) > 0)
sqlite3_bind_text(stmt, 6, buffer, -1, SQLITE_TRANSIENT);
else
sqlite3_bind_null(stmt, 6);
if(buffer != NULL)
free(buffer);
}
else
{
@ -237,11 +229,8 @@ void DB_save_queries(void)
saving_failed_before = true;
}
else
{
database = false;
}
dbclose();
dbclose();
return;
}
@ -257,11 +246,8 @@ void DB_save_queries(void)
saving_failed_before = true;
}
else
{
database = false;
}
dbclose();
dbclose();
return;
}
@ -274,9 +260,6 @@ void DB_save_queries(void)
db_update_counters(total, blocked);
}
// Close database
dbclose();
if(config.debug & DEBUG_DATABASE || saving_failed_before)
{
logg("Notice: Queries stored in long-term database: %u (took %.1f ms, last SQLite ID %li)", saved, timer_elapsed_msec(DATABASE_WRITE_TIMER), lastID);
@ -291,9 +274,8 @@ void DB_save_queries(void)
void delete_old_queries_in_DB(void)
{
// Open database
if(!dbopen())
if(!FTL_DB_avail())
{
logg("Failed to open long-term database when trying to delete old queries");
return;
}
@ -311,20 +293,14 @@ void delete_old_queries_in_DB(void)
// Print final message only if there is a difference
if((config.debug & DEBUG_DATABASE) || affected)
logg("Notice: Database size is %.2f MB, deleted %i rows", 1e-6*get_FTL_db_filesize(), affected);
// Close database
dbclose();
}
// Get most recent 24 hours data from long-term database
void DB_read_queries(void)
{
// Open database file
// Open database
if(!dbopen())
{
logg("Failed to open long-term database when trying to read queries");
return;
}
// Prepare request
// Get time stamp 24 hours in the past
@ -422,7 +398,16 @@ void DB_read_queries(void)
(long long)queryTimeStamp);
continue;
}
upstreamID = findUpstreamID(upstream, true);
// Get IP address and port of upstream destination
char serv_addr[INET6_ADDRSTRLEN] = { 0 };
unsigned int serv_port = 53;
// 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);
serv_addr[INET6_ADDRSTRLEN-1] = '\0';
upstreamID = findUpstreamID(serv_addr, (in_port_t)serv_port, true);
}
// Obtain IDs only after filtering which queries we want to keep
@ -559,5 +544,7 @@ void DB_read_queries(void)
// Finalize SQLite3 statement
sqlite3_finalize(stmt);
// Close database here, we have to reopen it later (after forking)
dbclose();
}

View File

@ -23,6 +23,8 @@
#include "main.h"
// reset_superclient()
#include "database/superclients.h"
// piholeFTLDB_reopen()
#include "database/common.h"
const char *querytypes[TYPE_MAX] = {"UNKNOWN", "A", "AAAA", "ANY", "SRV", "SOA", "PTR", "TXT",
"NAPTR", "MX", "DS", "RRSIG", "DNSKEY", "NS", "OTHER"};
@ -63,7 +65,7 @@ int findQueryID(const int id)
return -1;
}
int findUpstreamID(const char * upstreamString, const bool count)
int findUpstreamID(const char * upstreamString, const in_port_t port, const bool count)
{
// Go through already knows upstream servers and see if we used one of those
for(int upstreamID=0; upstreamID < counters->upstreams; upstreamID++)
@ -75,7 +77,7 @@ int findUpstreamID(const char * upstreamString, const bool count)
if(upstream == NULL)
continue;
if(strcmp(getstr(upstream->ippos), upstreamString) == 0)
if(strcmp(getstr(upstream->ippos), upstreamString) == 0 && upstream->port == port)
{
if(count)
{
@ -88,7 +90,7 @@ int findUpstreamID(const char * upstreamString, const bool count)
// This upstream server is not known
// Store ID
const int upstreamID = counters->upstreams;
logg("New upstream server: %s (%i/%u)", upstreamString, upstreamID, counters->upstreams_MAX);
logg("New upstream server: %s:%u (%i/%u)", upstreamString, port, upstreamID, counters->upstreams_MAX);
// Check struct size
memory_check(UPSTREAMS);
@ -119,6 +121,8 @@ int findUpstreamID(const char * upstreamString, const bool count)
upstream->namepos = 0; // 0 -> string with length zero
// This is a new upstream server
upstream->lastQuery = time(NULL);
// Store port
upstream->port = port;
// Increase counter by one
counters->upstreams++;
@ -461,6 +465,11 @@ void FTL_reset_per_client_domain_data(void)
void FTL_reload_all_domainlists(void)
{
lock_shm();
// (Re-)open FTL database connection
piholeFTLDB_reopen();
// Flush messages stored in the long-term database
flush_message_table();
@ -477,4 +486,6 @@ void FTL_reload_all_domainlists(void)
// Reset FTL's internal DNS cache storing whether a specific domain
// has already been validated for a specific user
FTL_reset_per_client_domain_data();
unlock_shm();
}

View File

@ -41,6 +41,7 @@ typedef struct {
typedef struct {
unsigned char magic;
bool new;
in_addr_t port;
int count;
int failed;
size_t ippos;
@ -88,7 +89,7 @@ typedef struct {
void strtolower(char *str);
int findQueryID(const int id);
int findUpstreamID(const char * upstream, const bool count);
int findUpstreamID(const char * upstream, const in_port_t port, const bool count);
int findDomainID(const char *domain, const bool count);
int findClientID(const char *client, const bool count, const bool superclient);
int findCacheID(int domainID, int clientID, enum query_types query_type);

View File

@ -10,7 +10,7 @@
#include "FTL.h"
#include "dhcp-discover.h"
// logg()
// logg(), format_time()
#include "log.h"
// read_FTLconf()
#include "config.h"
@ -226,27 +226,6 @@ static bool send_dhcp_discover(const int sock, const uint32_t xid, const char *i
return bytes > 0;
}
static void nice_time(char *buffer, unsigned long seconds)
{
unsigned int days = seconds / (60 * 60 * 24);
seconds -= days * (60 * 60 * 24);
unsigned int hours = seconds / (60 * 60);
seconds -= hours * (60 * 60);
unsigned int minutes = seconds / 60;
seconds %= 60;
buffer[0] = ' ';
buffer[1] = '\0';
if(days > 0)
sprintf(buffer + strlen(buffer), "%ud ", days);
if(hours > 0)
sprintf(buffer + strlen(buffer), "%uh ", hours);
if(minutes > 0)
sprintf(buffer + strlen(buffer), "%um ", minutes);
if(seconds > 0)
sprintf(buffer + strlen(buffer), "%lus ", seconds);
}
// adds a DHCP OFFER to list in memory
static void print_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet)
{
@ -360,7 +339,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet)
else
{
char buffer[32] = { 0 };
nice_time(buffer, lease_time);
format_time(buffer, lease_time, 0.0);
logg("%s(%lu seconds)", buffer, (unsigned long)lease_time);
}
}
@ -414,7 +393,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet)
else
{
char buffer[32] = { 0 };
nice_time(buffer, renewal_time);
format_time(buffer, renewal_time, 0.0);
logg("%s(%lu seconds)", buffer, (unsigned long)renewal_time);
}
}
@ -429,7 +408,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet)
else
{
char buffer[32] = { 0 };
nice_time(buffer, rebinding_time);
format_time(buffer, rebinding_time, 0.0);
logg("%s(%lu seconds)", buffer, (unsigned long)rebinding_time);
}
}

View File

@ -1782,6 +1782,11 @@ static void check_dns_listeners(time_t now)
for (listener = daemon->listeners; listener; listener = listener->next)
{
/******************** Pi-hole modification *******************/
FTL_next_iface(listener->iface ? listener->iface->name : NULL);
/*************************************************************/
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
receive_query(listener, now);

View File

@ -311,6 +311,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
FTL_forwarding_retried(forward->sentto, forward->log_id, daemon->log_id, true);
if (forward->sentto->sfd)
fd = forward->sentto->sfd->fd;
@ -347,7 +348,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
start = daemon->servers; /* at end of list, recycle */
header->id = htons(forward->new_id);
FTL_forwarding_failed(forward->sentto);
FTL_forwarding_retried(forward->sentto, forward->log_id, daemon->log_id, false);
}
else
{
@ -546,14 +547,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&start->addr.in.sin_addr, NULL);
FTL_forwarded(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&start->addr.in.sin_addr, daemon->log_display_id);
start, daemon->log_display_id);
}
else
{
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&start->addr.in6.sin6_addr, NULL);
FTL_forwarded(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&start->addr.in6.sin6_addr, daemon->log_display_id);
start, daemon->log_display_id);
}
start->queries++;
forwarded = 1;
@ -2178,14 +2179,14 @@ unsigned char *tcp_request(int confd, time_t now,
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&last_server->addr.in.sin_addr, NULL);
FTL_forwarded(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(union all_addr *)&last_server->addr.in.sin_addr, daemon->log_display_id);
last_server, daemon->log_display_id);
}
else
{
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&last_server->addr.in6.sin6_addr, NULL);
FTL_forwarded(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(union all_addr *)&last_server->addr.in6.sin6_addr, daemon->log_display_id);
last_server, daemon->log_display_id);
}
#ifdef HAVE_DNSSEC

View File

@ -241,6 +241,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
else
continue;
/************************** Pi-hole modification **************************/
FTL_log_helper(1, action_str);
/**************************************************************************/
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
@ -640,6 +643,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
close(pipefd[0]);
/**************************** Pi-hole modification ****************************/
FTL_log_helper(5, daemon->lease_change_command, action_str,
(is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
daemon->addrbuff, hostname);
/******************************************************************************/
p = strrchr(daemon->lease_change_command, '/');
if (err == 0)
{
@ -650,6 +659,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
err = errno;
}
/* failed, send event so the main process logs the problem */
/**************************** Pi-hole modification ****************************/
FTL_log_helper(2, daemon->lease_change_command, strerror(err));
/******************************************************************************/
send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
_exit(0);
}

View File

@ -70,8 +70,18 @@ const char flagnames[][12] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWAR
// Store interface the next query will come from for later usage
void FTL_next_iface(const char *newiface)
{
strncpy(next_iface, newiface, sizeof(next_iface)-1);
next_iface[sizeof(next_iface)-1] = '\0';
if(newiface != NULL)
{
// Copy interface name if available
strncpy(next_iface, newiface, sizeof(next_iface)-1);
next_iface[sizeof(next_iface)-1] = '\0';
}
else
{
// Use dummy when interface record is not available
next_iface[0] = '-';
next_iface[1] = '\0';
}
}
static bool check_domain_blocked(const char *domain, const int clientID,
@ -535,7 +545,11 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
char *domainString = strdup(name);
strtolower(domainString);
// Get client IP address (can be overwritten by EDNS(0) client subnet (ECS) data)
// Get client IP address
// The requestor's IP address can be rewritten using EDNS(0) client
// subnet (ECS) data), however, we do not rewrite the IPs ::1 and
// 127.0.0.1 to avoid queries originating from localhost of the
// *distant* machine as queries coming from the *local* machine
const sa_family_t family = (flags & F_IPV4) ? AF_INET : AF_INET6;
char clientIP[ADDRSTRLEN] = { 0 };
if(config.edns0_ecs && edns->client_set)
@ -751,7 +765,7 @@ void _FTL_get_blocking_metadata(union all_addr **addrp, unsigned int *flags, con
}
}
void _FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, const int id,
void _FTL_forwarded(const unsigned int flags, const char *name, const struct server *serv, const int id,
const char* file, const int line)
{
// Save that this query got forwarded to an upstream server
@ -759,19 +773,33 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const union all_
// Lock shared memory
lock_shm();
// Get forward destination IP address
// Get forward destination IP address and port
in_port_t upstreamPort = 53;
char dest[ADDRSTRLEN];
// If addr == NULL, we will only duplicate an empty string instead of uninitialized memory
dest[0] = '\0';
if(addr != NULL)
inet_ntop((flags & F_IPV4) ? AF_INET : AF_INET6, addr, dest, ADDRSTRLEN);
if(serv != NULL)
{
if(serv->addr.sa.sa_family == AF_INET)
{
inet_ntop(AF_INET, &serv->addr.in.sin_addr, dest, ADDRSTRLEN);
upstreamPort = ntohs(serv->addr.in.sin_port);
}
else
{
inet_ntop(AF_INET6, &serv->addr.in6.sin6_addr, dest, ADDRSTRLEN);
upstreamPort = ntohs(serv->addr.in6.sin6_port);
}
}
// Convert upstreamIP to lower case
char *upstreamIP = strdup(dest);
strtolower(upstreamIP);
// Debug logging
if(config.debug & DEBUG_QUERIES) logg("**** forwarded %s to %s (ID %i, %s:%i)", name, upstreamIP, id, file, line);
if(config.debug & DEBUG_QUERIES)
logg("**** forwarded %s to %s#%u (ID %i, %s:%i)",
name, upstreamIP, upstreamPort, id, file, line);
// Save status and upstreamID in corresponding query identified by dnsmasq's ID
const int queryID = findQueryID(id);
@ -803,7 +831,7 @@ void _FTL_forwarded(const unsigned int flags, const char *name, const union all_
// Get ID of upstream destination, create new upstream record
// if not found in current data structure
const int upstreamID = findUpstreamID(upstreamIP, true);
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort, true);
query->upstreamID = upstreamID;
// Get time index for this query
@ -873,6 +901,9 @@ void FTL_dnsmasq_reload(void)
logg("Reloading DNS cache");
// (Re-)open FTL database connection
piholeFTLDB_reopen();
// Request reload the privacy level
set_event(RELOAD_PRIVACY_LEVEL);
@ -1718,7 +1749,7 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw)
}
// Start database thread if database is used
if(database && pthread_create( &DBthread, &attr, DB_thread, NULL ) != 0)
if(pthread_create( &DBthread, &attr, DB_thread, NULL ) != 0)
{
logg("Unable to open database thread. Exiting...");
exit(EXIT_FAILURE);
@ -1742,15 +1773,34 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw)
// Chown files if FTL started as user root but a dnsmasq config
// option states to run as a different user/group (e.g. "nobody")
if(ent_pw != NULL && getuid() == 0)
if(getuid() == 0)
{
if(chown(FTLfiles.log, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
logg("Setting ownership (%i:%i) of %s failed: %s (%i)",
ent_pw->pw_uid, ent_pw->pw_gid, FTLfiles.log, strerror(errno), errno);
if(database && chown(FTLfiles.FTL_db, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
logg("Setting ownership (%i:%i) of %s failed: %s (%i)",
ent_pw->pw_uid, ent_pw->pw_gid, FTLfiles.FTL_db, strerror(errno), errno);
chown_all_shmem(ent_pw);
if(ent_pw != NULL)
{
logg("INFO: FTL is going to drop from root to user %s (UID %d)",
ent_pw->pw_name, (int)ent_pw->pw_uid);
if(chown(FTLfiles.log, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
logg("Setting ownership (%i:%i) of %s failed: %s (%i)",
ent_pw->pw_uid, ent_pw->pw_gid, FTLfiles.log, strerror(errno), errno);
if(chown(FTLfiles.FTL_db, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
logg("Setting ownership (%i:%i) of %s failed: %s (%i)",
ent_pw->pw_uid, ent_pw->pw_gid, FTLfiles.FTL_db, strerror(errno), errno);
chown_all_shmem(ent_pw);
}
else
{
logg("INFO: FTL is running as root");
}
}
else
{
uid_t uid;
struct passwd *current_user;
if ((current_user = getpwuid(uid = geteuid())) != NULL)
logg("INFO: FTL is running as user %s (UID %d)",
current_user->pw_name, (int)current_user->pw_uid);
else
logg("INFO: Failed to obtain information about FTL user");
}
// Obtain DNS port from dnsmasq daemon
@ -1775,7 +1825,7 @@ void getCacheInformation(const int *sock)
// looked up for the longest time is evicted.
}
void _FTL_forwarding_failed(const struct server *server, const char* file, const int line)
void FTL_forwarding_retried(const struct server *serv, const int oldID, const int newID, const bool dnssec)
{
// Forwarding to upstream server failed
@ -1784,20 +1834,35 @@ void _FTL_forwarding_failed(const struct server *server, const char* file, const
// Try to obtain destination IP address if available
char dest[ADDRSTRLEN];
if(server->addr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &server->addr.in.sin_addr, dest, ADDRSTRLEN);
else
inet_ntop(AF_INET6, &server->addr.in6.sin6_addr, dest, ADDRSTRLEN);
in_port_t upstreamPort = 53;
dest[0] = '\0';
if(serv != NULL)
{
if(serv->addr.sa.sa_family == AF_INET)
{
inet_ntop(AF_INET, &serv->addr.in.sin_addr, dest, ADDRSTRLEN);
upstreamPort = ntohs(serv->addr.in.sin_port);
}
else
{
inet_ntop(AF_INET6, &serv->addr.in6.sin6_addr, dest, ADDRSTRLEN);
upstreamPort = ntohs(serv->addr.in6.sin6_port);
}
}
// Convert upstream to lower case
char *upstreamIP = strdup(dest);
strtolower(upstreamIP);
// Get upstream ID
const int upstreamID = findUpstreamID(upstreamIP, false);
const int upstreamID = findUpstreamID(upstreamIP, upstreamPort, false);
// Possible debugging information
if(config.debug & DEBUG_QUERIES) logg("**** forwarding to %s (ID %i, %s:%i) FAILED", dest, upstreamID, file, line);
if(config.debug & DEBUG_QUERIES)
{
logg("**** RETRIED query %i as %i to %s (ID %i)",
oldID, newID, dest, upstreamID);
}
// Get upstream pointer
upstreamsData* upstream = getUpstream(upstreamID, true);
@ -1806,6 +1871,36 @@ void _FTL_forwarding_failed(const struct server *server, const char* file, const
if(upstream != NULL)
upstream->failed++;
// 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)
const int queryID = findQueryID(dnssec ? newID : oldID);
if(queryID >= 0)
{
// Get query pointer
queriesData* query = getQuery(queryID, true);
// Set retried status
if(query != NULL)
{
if(dnssec)
{
// There is point in retrying the query when
// we've already got an answer to this query,
// but we're awaiting keys for DNSSEC
// validation. We're retrying the DNSSEC query
// instead
query->status = QUERY_RETRIED_DNSSEC;
}
else
{
// Normal query retry due to answer not arriving
// soon enough at the requestor
query->status = QUERY_RETRIED;
}
}
}
// Clean up and unlock shared memory
free(upstreamIP);
unlock_shm();

View File

@ -24,8 +24,8 @@ void FTL_next_iface(const char *newiface);
#define FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, edns, proto) _FTL_new_query(flags, name, blockingreason, addr, types, qtype, id, edns, proto, __FILE__, __LINE__)
bool _FTL_new_query(const unsigned int flags, const char *name, const char** blockingreason, const union all_addr *addr, const char *types, const unsigned short qtype, const int id, const struct edns_data *edns, enum protocol proto, const char* file, const int line);
#define FTL_forwarded(flags, name, addr, id) _FTL_forwarded(flags, name, addr, id, __FILE__, __LINE__)
void _FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
#define FTL_forwarded(flags, name, serv, id) _FTL_forwarded(flags, name, serv, id, __FILE__, __LINE__)
void _FTL_forwarded(const unsigned int flags, const char *name, const struct server *serv, const int id, const char* file, const int line);
#define FTL_reply(flags, name, addr, id) _FTL_reply(flags, name, addr, id, __FILE__, __LINE__)
void _FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
@ -39,8 +39,7 @@ void _FTL_dnssec(const int status, const int id, const char* file, const int lin
#define FTL_header_analysis(header4, rcode, id) _FTL_header_analysis(header4, rcode, id, __FILE__, __LINE__)
void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, const int id, const char* file, const int line);
#define FTL_forwarding_failed(server) _FTL_forwarding_failed(server, __FILE__, __LINE__)
void _FTL_forwarding_failed(const struct server *server, const char* file, const int line);
void FTL_forwarding_retried(const struct server *server, const int oldID, const int newID, const bool dnssec);
#define FTL_upstream_error(rcode, id) _FTL_upstream_error(rcode, id, __FILE__, __LINE__)
void _FTL_upstream_error(const unsigned int rcode, const int id, const char* file, const int line);

View File

@ -176,34 +176,34 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
if (code == EDNS0_ECS && config.edns0_ecs)
{
// EDNS(0) CLIENT SUBNET
// RFC 7871 Client Subnet in DNS Queries 6. Option Format
// This protocol uses an EDNS0 [RFC6891] option to include client
// address information in DNS messages. The option is structured as
// follows:
//
// +0 (MSB) +1 (LSB)
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 0: | OPTION-CODE |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 2: | OPTION-LENGTH |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 4: | FAMILY |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// RFC 7871 Client Subnet in DNS Queries 6. Option Format
// This protocol uses an EDNS0 [RFC6891] option to include client
// address information in DNS messages. The option is structured as
// follows:
//
// +0 (MSB) +1 (LSB)
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 0: | OPTION-CODE |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 2: | OPTION-LENGTH |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 4: | FAMILY |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
short family;
GETSHORT(family, p);
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 6: | SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 6: | SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
unsigned char source_netmask = *p++;
p++; // We are not interested in the scope prefix-length. It MUST be 0 in queries
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 8: | ADDRESS... /
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 8: | ADDRESS... /
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
union all_addr addr = {{ 0 }};
if(family == 1 && optlen > 4) // IPv4
memcpy(&addr.addr4.s_addr, p, optlen - 4);
else if(family == 2 && optlen > 4) // IPv6
memcpy(addr.addr6.s6_addr, p, optlen - 4);
if(family == 1 && optlen == 4 + sizeof(addr.addr4.s_addr)) // IPv4
memcpy(&addr.addr4.s_addr, p, sizeof(addr.addr4.s_addr));
else if(family == 2 && optlen == 4 + sizeof(addr.addr6.s6_addr)) // IPv6
memcpy(addr.addr6.s6_addr, p, sizeof(addr.addr6.s6_addr));
else
continue;
@ -213,18 +213,31 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
char ipaddr[ADDRSTRLEN] = { 0 };
inet_ntop(family == 1 ? AF_INET : AF_INET6, &addr.addr4.s_addr, ipaddr, sizeof(ipaddr));
if(config.debug & DEBUG_EDNS0)
logg("EDNS(0) CLIENT SUBNET: %s/%u", ipaddr, source_netmask);
// Only use /32 (IPv4) and /128 (IPv6) addresses
if(!(family == 1 && source_netmask == 32) &&
!(family == 2 && source_netmask == 128))
continue;
// Copy data to edns struct
edns->client_set = true;
strncpy(edns->client, ipaddr, ADDRSTRLEN);
edns->client[ADDRSTRLEN-1] = '\0';
// Only set the address as useful when it is not the
// loopback address of the distant machine (127.0.0.0/8 or ::1)
if((family == 1 && (ntohl(addr.addr4.s_addr) & 0xFF000000) == 0x7F000000) ||
(family == 2 && IN6_IS_ADDR_LOOPBACK(&addr.addr6)))
{
if(config.debug & DEBUG_EDNS0)
logg("EDNS(0) CLIENT SUBNET: Skipped %s/%u (IPv%u loopback address)",
ipaddr, source_netmask, family == 1 ? 4 : 6);
}
else
{
edns->client_set = true;
if(config.debug & DEBUG_EDNS0)
logg("EDNS(0) CLIENT SUBNET: %s/%u - OK (IPv%u)",
ipaddr, source_netmask, family == 1 ? 4 : 6);
}
}
else if(code == EDNS0_COOKIE && optlen == 8)
{

View File

@ -40,6 +40,8 @@ enum query_status {
QUERY_GRAVITY_CNAME,
QUERY_REGEX_CNAME,
QUERY_BLACKLIST_CNAME,
QUERY_RETRIED,
QUERY_RETRIED_DNSSEC,
QUERY_STATUS_MAX
} __attribute__ ((packed));
@ -136,6 +138,7 @@ enum debug_flags {
DEBUG_CLIENTS = (1 << 17), /* 00000000 00000010 00000000 00000000 */
DEBUG_SUPERCLIENTS = (1 << 18), /* 00000000 00000100 00000000 00000000 */
DEBUG_EVENTS = (1 << 19), /* 00000000 00001000 00000000 00000000 */
DEBUG_HELPER = (1 << 20), /* 00000000 00010000 00000000 00000000 */
} __attribute__ ((packed));
enum events {
@ -144,7 +147,8 @@ enum events {
RERESOLVE_HOSTNAMES,
REIMPORT_SUPERCLIENTS,
PARSE_NEIGHBOR_CACHE,
RERESOLVE_DATABASE_NAMES,
EVENTS_MAX
} __attribute__ ((packed));
#endif // ENUMS_H
#endif // ENUMS_H

View File

@ -96,8 +96,10 @@ static const char *eventtext(const enum events event)
return "REIMPORT_SUPERCLIENTS";
case PARSE_NEIGHBOR_CACHE:
return "PARSE_NEIGHBOR_CACHE";
case RERESOLVE_DATABASE_NAMES:
return "RERESOLVE_DATABASE_NAMES";
case EVENTS_MAX: // fall through
default:
return "UNKNOWN";
}
}
}

View File

@ -100,7 +100,9 @@ void *GC_thread(void *val)
// Unknown (?)
counters->unknown--;
break;
case QUERY_FORWARDED:
case QUERY_FORWARDED: // (fall through)
case QUERY_RETRIED: // (fall through)
case QUERY_RETRIED_DNSSEC:
// Forwarded to an upstream DNS server
// Adjust counters
counters->forwarded--;

View File

@ -166,6 +166,44 @@ void _FTL_log(const bool newline, const char *format, ...)
pthread_mutex_unlock(&lock);
}
// Log helper activity (may be script or lua)
void FTL_log_helper(const unsigned char n, ...)
{
// Only log helper debug messages if enabled
if(!(config.debug & DEBUG_HELPER))
return;
// Extract all variable arguments
va_list args;
char *arg[n];
va_start(args, n);
for(unsigned char i = 0; i < n; i++)
arg[i] = strdup(va_arg(args, char*));
va_end(args);
// Select appropriate logging format
switch (n)
{
case 1:
logg("Script: Starting helper for action \"%s\"", arg[0]);
break;
case 2:
logg("Script: FAILED to execute \"%s\": %s", arg[0], arg[1]);
break;
case 5:
logg("Script: Executing \"%s\" with arguments: \"%s %s %s %s\"",
arg[0], arg[1], arg[2], arg[3], arg[4]);
break;
default:
logg("ERROR: Unsupported number of arguments passed to FTL_log_helper(): %u", n);
break;
}
// Free allocated memory
for(unsigned char i = 0; i < n; i++)
free(arg[i]);
}
void format_memory_size(char * const prefix, const unsigned long long int bytes,
double * const formated)
{
@ -183,6 +221,38 @@ void format_memory_size(char * const prefix, const unsigned long long int bytes,
strcpy(prefix, prefixes[i]);
}
// Human-readable time
void format_time(char buffer[42], unsigned long seconds, double milliseconds)
{
unsigned long umilliseconds = 0;
if(milliseconds > 0)
{
seconds = milliseconds / 1000;
umilliseconds = (unsigned long)milliseconds % 1000;
}
const unsigned int days = seconds / (60 * 60 * 24);
seconds -= days * (60 * 60 * 24);
const unsigned int hours = seconds / (60 * 60);
seconds -= hours * (60 * 60);
const unsigned int minutes = seconds / 60;
seconds %= 60;
buffer[0] = ' ';
buffer[1] = '\0';
if(days > 0)
sprintf(buffer + strlen(buffer), "%ud ", days);
if(hours > 0)
sprintf(buffer + strlen(buffer), "%uh ", hours);
if(minutes > 0)
sprintf(buffer + strlen(buffer), "%um ", minutes);
if(seconds > 0)
sprintf(buffer + strlen(buffer), "%lus ", seconds);
// Only append milliseconds when the timer value is less than 10 seconds
if((days + hours + minutes) == 0 && seconds < 10 && umilliseconds > 0)
sprintf(buffer + strlen(buffer), "%lums ", umilliseconds);
}
void log_counter_info(void)
{
logg(" -> Total DNS queries: %i", counters->queries);

View File

@ -18,6 +18,7 @@ void open_FTL_log(const bool test);
void log_counter_info(void);
void format_memory_size(char * const prefix, unsigned long long int bytes,
double * const formated);
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);
void get_timestr(char * const timestring, const time_t timein);
@ -29,5 +30,6 @@ const char *get_ordinal_suffix(unsigned int number) __attribute__ ((const));
#define logg_sameline(format, ...) _FTL_log(false, format, ## __VA_ARGS__)
void _FTL_log(const bool newline, const char* format, ...) __attribute__ ((format (gnu_printf, 2, 3)));
void log_ctrl(bool vlog, bool vstdout);
void FTL_log_helper(const unsigned char n, ...);
#endif //LOG_H

View File

@ -80,7 +80,7 @@ int main (int argc, char* argv[])
db_init();
// Try to import queries from long-term database if available
if(database && config.DBimport)
if(config.DBimport)
DB_read_queries();
log_counter_info();
@ -107,8 +107,8 @@ int main (int argc, char* argv[])
if(ipv6telnet) pthread_cancel(telnet_listenthreadv6);
pthread_cancel(socket_listenthread);
// Save new queries to database
if(database)
// Save new queries to database (if database is used)
if(use_database())
{
DB_save_queries();
logg("Finished final database update");
@ -132,6 +132,9 @@ int main (int argc, char* argv[])
//Remove PID file
removepid();
logg("########## FTL terminated after %e s! ##########", 1e-3*timer_elapsed_msec(EXIT_TIMER));
char buffer[42] = { 0 };
format_time(buffer, 0, timer_elapsed_msec(EXIT_TIMER));
logg("########## FTL terminated after%s! ##########", buffer);
return exit_code;
}

View File

@ -340,6 +340,10 @@ static void free_regex(void)
}
}
// This function does three things:
// 1. Allocate additional memory if required
// 2. Reset all regex to false for this client
// 3. Load regex enabled/disabled state
void reload_per_client_regex(const int clientID, clientsData *client)
{
// Ensure there is enough memory in the shared memory object
@ -455,8 +459,9 @@ void read_regex_from_database(void)
// Read and compile regex whitelist
read_regex_table(REGEX_WHITELIST);
// Load per-client regex data, not all of the regex read and compiled
// above will also be used by all clients
// Loop over all clients and ensure we have enough space and load
// per-client regex data, not all of the regex read and compiled above
// will also be used by all clients
for(int clientID = 0; clientID < counters->clients; clientID++)
{
// Get client pointer

View File

@ -531,7 +531,10 @@ void *DNSclient_thread(void *val)
// Run every hour to update possibly changed client host names
if(resolver_ready && (time(NULL) % RERESOLVE_INTERVAL == 0))
set_event(RERESOLVE_HOSTNAMES);
{
set_event(RERESOLVE_HOSTNAMES); // done below
set_event(RERESOLVE_DATABASE_NAMES); // done in database thread
}
// Process resolver related event queue elements
if(get_and_clear_event(RERESOLVE_HOSTNAMES))
@ -540,9 +543,6 @@ void *DNSclient_thread(void *val)
resolveClients(false);
// Try to resolve all upstream destination host names (onlynew=false)
resolveUpstreams(false);
// Try to resolve host names from clients in the network table
// which have empty/undefined host names
resolveNetworkTableNames();
// Prevent immediate re-run of this routine
sleepms(500);
}

View File

@ -181,7 +181,7 @@ size_t addstr(const char *str)
// Reserve additional memory if necessary
if(shmSettings->next_str_pos + len > shm_strings.size &&
!realloc_shm(&shm_strings, shm_strings.size + pagesize, true))
!realloc_shm(&shm_strings, shm_strings.size + pagesize, sizeof(char), true))
return 0;
// Store new string buffer size in corresponding counters entry
@ -236,22 +236,22 @@ static pthread_mutex_t create_mutex(void) {
static void remap_shm(void)
{
// Remap shared object pointers which might have changed
realloc_shm(&shm_queries, counters->queries_MAX*sizeof(queriesData), false);
realloc_shm(&shm_queries, counters->queries_MAX, sizeof(queriesData), false);
queries = (queriesData*)shm_queries.ptr;
realloc_shm(&shm_domains, counters->domains_MAX*sizeof(domainsData), false);
realloc_shm(&shm_domains, counters->domains_MAX, sizeof(domainsData), false);
domains = (domainsData*)shm_domains.ptr;
realloc_shm(&shm_clients, counters->clients_MAX*sizeof(clientsData), false);
realloc_shm(&shm_clients, counters->clients_MAX, sizeof(clientsData), false);
clients = (clientsData*)shm_clients.ptr;
realloc_shm(&shm_upstreams, counters->upstreams_MAX*sizeof(upstreamsData), false);
realloc_shm(&shm_upstreams, counters->upstreams_MAX, sizeof(upstreamsData), false);
upstreams = (upstreamsData*)shm_upstreams.ptr;
realloc_shm(&shm_dns_cache, counters->dns_cache_MAX*sizeof(DNSCacheData), false);
realloc_shm(&shm_dns_cache, counters->dns_cache_MAX, sizeof(DNSCacheData), false);
dns_cache = (DNSCacheData*)shm_dns_cache.ptr;
realloc_shm(&shm_strings, counters->strings_MAX, false);
realloc_shm(&shm_strings, counters->strings_MAX, sizeof(char), false);
// strings are not exposed by a global pointer
// Update local counter to reflect that we absorbed this change
@ -441,14 +441,15 @@ SharedMemory create_shm(const char *name, const size_t size)
exit(EXIT_FAILURE);
}
// Resize shared memory file
ret = ftruncate(fd, size);
// Check for `ftruncate` error
if(ret == -1)
// Allocate shared memory object to specified size
// Using fallocate() will ensure that there's actually space for
// this file. Otherwise we end up with a sparse file that can give
// SIGBUS if we run out of space while writing to it.
ret = fallocate(fd, 0, 0U, size);
if(ret != 0)
{
logg("FATAL: create_shm(): ftruncate(%i, %zu): Failed to resize shared memory object \"%s\": %s",
fd, size, sharedMemory.name, strerror(errno));
logg("FATAL: create_shm(): Failed to resize \"%s\" (%i) to %zu: %s (%i)",
sharedMemory.name, fd, size, strerror(errno), ret);
exit(EXIT_FAILURE);
}
@ -515,8 +516,8 @@ void *enlarge_shmem_struct(const char type)
return 0;
}
// Reallocate enough space for 4096 instances of requested object
realloc_shm(sharedMemory, sharedMemory->size + allocation_step*sizeofobj, true);
// Reallocate enough space for requested object
realloc_shm(sharedMemory, sharedMemory->size/sizeofobj + allocation_step, sizeofobj, true);
// Add allocated memory to corresponding counter
*counter += allocation_step;
@ -524,8 +525,10 @@ void *enlarge_shmem_struct(const char type)
return sharedMemory->ptr;
}
bool realloc_shm(SharedMemory *sharedMemory, const size_t size, const bool resize)
bool realloc_shm(SharedMemory *sharedMemory, const size_t size1, const size_t size2, const bool resize)
{
// Absolute target size
const size_t size = size1 * size2;
// Check if we can skip this routine as nothing is to be done
// when an object is not to be resized and its size didn't
// change elsewhere
@ -535,14 +538,15 @@ bool realloc_shm(SharedMemory *sharedMemory, const size_t size, const bool resiz
// Log that we are doing something here
char df[64] = { 0 };
const int percentage = get_dev_shm_usage(df);
// Log output
if(resize)
{
logg("Resizing \"%s\" from %zu to %zu (%s)", sharedMemory->name, sharedMemory->size, size, df);
}
logg("Resizing \"%s\" from %zu to (%zu * %zu) == %zu (%s)",
sharedMemory->name, sharedMemory->size, size1, size2, size, df);
else
{
logg("Remapping \"%s\" from %zu to %zu", sharedMemory->name, sharedMemory->size, size);
}
logg("Remapping \"%s\" from %zu to (%zu * %zu) == %zu",
sharedMemory->name, sharedMemory->size, size1, size2, size);
if(percentage > SHMEM_WARN_LIMIT)
logg("WARNING: More than %u%% of "SHMEM_PATH" is used", SHMEM_WARN_LIMIT);
@ -561,16 +565,20 @@ bool realloc_shm(SharedMemory *sharedMemory, const size_t size, const bool resiz
exit(EXIT_FAILURE);
}
// Truncate shared memory object to specified size
const int result = ftruncate(fd, size);
if(result == -1) {
logg("FATAL: realloc_shm(): ftruncate(%i, %zu): Failed to resize \"%s\": %s",
fd, size, sharedMemory->name, strerror(errno));
// Allocate shared memory object to specified size
// Using fallocate() will ensure that there's actually space for
// this file. Otherwise we end up with a sparse file that can give
// SIGBUS if we run out of space while writing to it.
const int ret = fallocate(fd, 0, 0U, size);
if(ret != 0)
{
logg("FATAL: realloc_shm(): Failed to resize \"%s\" (%i) to %zu: %s (%i)",
sharedMemory->name, fd, size, strerror(errno), ret);
exit(EXIT_FAILURE);
}
// Close shared memory object file descriptor as it is no longer
// needed after having called ftruncate()
// needed after having called fallocate()
close(fd);
// Update shm counters to indicate that at least one shared memory object changed
@ -758,7 +766,7 @@ void add_per_client_regex(unsigned int clientID)
counters->num_regex[REGEX_WHITELIST];
const size_t size = counters->clients * num_regex_tot;
if(size > shm_per_client_regex.size &&
realloc_shm(&shm_per_client_regex, size, true))
realloc_shm(&shm_per_client_regex, counters->clients, num_regex_tot, true))
{
reset_per_client_regex(clientID);
}
@ -769,11 +777,12 @@ bool get_per_client_regex(const int clientID, const int regexID)
const unsigned int num_regex_tot = counters->num_regex[REGEX_BLACKLIST] +
counters->num_regex[REGEX_WHITELIST];
const unsigned int id = clientID * num_regex_tot + regexID;
const unsigned int maxval = counters->clients * num_regex_tot;
const size_t maxval = shm_per_client_regex.size / sizeof(bool);
if(id > maxval)
{
logg("ERROR: get_per_client_regex(%d,%d): Out of bounds (%d > %d * %d == %d)!",
clientID, regexID, id, counters->clients-1, num_regex_tot, maxval);
logg("ERROR: get_per_client_regex(%d, %d): Out of bounds (%d > %d * %d, shm_per_client_regex.size = %zd)!",
clientID, regexID,
id, counters->clients, num_regex_tot, maxval);
return false;
}
return ((bool*) shm_per_client_regex.ptr)[id];
@ -784,12 +793,12 @@ void set_per_client_regex(const int clientID, const int regexID, const bool valu
const unsigned int num_regex_tot = counters->num_regex[REGEX_BLACKLIST] +
counters->num_regex[REGEX_WHITELIST];
const unsigned int id = clientID * num_regex_tot + regexID;
const unsigned int maxval = counters->clients * num_regex_tot;
const size_t maxval = shm_per_client_regex.size / sizeof(bool);
if(id > maxval)
{
logg("ERROR: set_per_client_regex(%d,%d,%s): Out of bounds (%d > %d * %d == %d)!",
logg("ERROR: set_per_client_regex(%d, %d, %s): Out of bounds (%d > %d * %d, shm_per_client_regex.size = %zd)!",
clientID, regexID, value ? "true" : "false",
id, counters->clients-1, num_regex_tot, maxval);
id, counters->clients, num_regex_tot, maxval);
return;
}
((bool*) shm_per_client_regex.ptr)[id] = value;

View File

@ -69,10 +69,11 @@ SharedMemory create_shm(const char *name, const size_t size);
/// Reallocate shared memory
///
/// \param sharedMemory the shared memory struct
/// \param size the new size
/// \param size1 the new size (factor 1)
/// \param size2 the new size (factor 2)
/// \param resize whether the object should be resized or only remapped
/// \return if reallocation was successful
bool realloc_shm(SharedMemory *sharedMemory, const size_t size, const bool resize);
bool realloc_shm(SharedMemory *sharedMemory, const size_t size1, const size_t size2, const bool resize);
/// Disconnect from shared memory. If there are no other connections to shared memory, it will be deleted.
///

View File

@ -22,7 +22,7 @@ void timer_start(const enum timers i)
logg("Code error: Timer %i not defined in timer_start().", i);
exit(EXIT_FAILURE);
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0[i]);
clock_gettime(CLOCK_REALTIME, &t0[i]);
}
static struct timespec diff(struct timespec start, struct timespec end)
@ -31,7 +31,7 @@ static struct timespec diff(struct timespec start, struct timespec end)
if(end.tv_nsec-start.tv_nsec < 0L)
{
diff.tv_sec = end.tv_sec - start.tv_sec - 1; // subtract one second here...
diff.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; // ...we have to add it here
diff.tv_nsec = end.tv_nsec - start.tv_nsec + 1000000000L; // ...we have to add it here
}
else
{
@ -49,27 +49,11 @@ double timer_elapsed_msec(const enum timers i)
exit(EXIT_FAILURE);
}
struct timespec t1, td;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
clock_gettime(CLOCK_REALTIME, &t1);
td = diff(t0[i], t1);
return td.tv_sec * 1e3 + td.tv_nsec * 1e-6;
}
unsigned long timer_elapsed_usec(const enum timers i)
{
struct timespec t1, td;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
td = diff(t0[i], t1);
return td.tv_sec * 1000000L + td.tv_nsec / 1000;
}
unsigned long long timer_elapsed_nsec(const enum timers i)
{
struct timespec t1, td;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
td = diff(t0[i], t1);
return td.tv_sec * 1000000000LL + td.tv_nsec;
}
void sleepms(const int milliseconds)
{
struct timeval tv;

View File

@ -25,8 +25,6 @@ enum timers {
void timer_start(const enum timers i);
double timer_elapsed_msec(const enum timers i);
unsigned long timer_elapsed_usec(const enum timers i);
unsigned long long timer_elapsed_nsec(const enum timers i);
void sleepms(const int milliseconds);
#endif //TIMERS_H

View File

@ -64,31 +64,59 @@ check_file() {
echo "Binary classification checks: OK (${1})"
}
check_static() {
if readelf -l ./pihole-FTL | grep -q INTERP; then
echo "Not a static executable, depends on dynamic interpreter"
ldd ./pihole-FTL
exit 1
fi
echo "Static executable check: OK"
}
if [[ "${CIRCLE_JOB}" == "x86_64" ]]; then
check_machine "ELF64" "Advanced Micro Devices X86-64"
check_libs "[librt.so.1] [libpthread.so.0] [libc.so.6]"
check_file "ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped"
check_file "ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped"
elif [[ "${CIRCLE_JOB}" == "x86_64-musl" ]]; then
check_machine "ELF64" "Advanced Micro Devices X86-64"
check_libs "" # No dependency on any shared library is intended
check_file "ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, with debug_info, not stripped"
check_static # Binary should not rely on any dynamic interpreter
check_file "ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped"
elif [[ "${CIRCLE_JOB}" == "x86_32" ]]; then
check_machine "ELF32" "Intel 80386"
check_libs "[librt.so.1] [libpthread.so.0] [libc.so.6]"
check_file "ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, not stripped"
check_file "ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped"
elif [[ "${CIRCLE_JOB}" == "aarch64" ]]; then
check_machine "ELF64" "AArch64"
check_libs "[librt.so.1] [libpthread.so.0] [libc.so.6] [ld-linux-aarch64.so.1]"
check_file "ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, not stripped"
check_file "ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, with debug_info, not stripped"
elif [[ "${CIRCLE_JOB}" == "arm" ]]; then
elif [[ "${CIRCLE_JOB}" == "armv4t" ]]; then
check_machine "ELF32" "ARM"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux.so.3]"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, not stripped"
check_CPU_arch "v4T"
check_FP_arch "" # No specified FP arch
elif [[ "${CIRCLE_JOB}" == "armv5te" ]]; then
check_machine "ELF32" "ARM"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux.so.3]"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, with debug_info, not stripped"
check_CPU_arch "v5TE"
check_FP_arch "" # No specified FP arch
elif [[ "${CIRCLE_JOB}" == "armv6hf" ]]; then
check_machine "ELF32" "ARM"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux-armhf.so.3]"
@ -97,27 +125,29 @@ elif [[ "${CIRCLE_JOB}" == "arm" ]]; then
check_CPU_arch "v6"
check_FP_arch "VFPv2"
elif [[ "${CIRCLE_JOB}" == "armhf" ]]; then
elif [[ "${CIRCLE_JOB}" == "armv7hf" ]]; then
check_machine "ELF32" "ARM"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux-armhf.so.3]"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, not stripped"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, with debug_info, not stripped"
check_CPU_arch "v7"
check_FP_arch "VFPv3-D16"
elif [[ "${CIRCLE_JOB}" == "arm-qemu" ]]; then
elif [[ "${CIRCLE_JOB}" == "armv8a" ]]; then
check_machine "ELF32" "ARM"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux.so.3]"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 3.2.0, not stripped"
check_libs "[librt.so.1] [libgcc_s.so.1] [libpthread.so.0] [libc.so.6] [ld-linux-armhf.so.3]"
check_file "ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, with debug_info, not stripped"
check_CPU_arch "v6"
check_FP_arch "" # No specified FP arch
check_CPU_arch "v8"
check_FP_arch "VFPv3-D16"
else
echo "Invalid job ${CIRCLE_JOB}"
exit 1
fi
exit 0

View File

@ -975,3 +975,40 @@
[[ ${lines[0]} == "1" ]]
}
@test "EDNS(0) ECS skipped for loopback address (IPv4)" {
# Get number of lines in the log before the test
before="$(grep -c ^ /var/log/pihole-FTL.log)"
# Run test command
run bash -c 'dig localhost +short +subnet=127.0.0.1/32 @127.0.0.1'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "127.0.0.1" ]]
[[ $status == 0 ]]
# Get number of lines in the log after the test
after="$(grep -c ^ /var/log/pihole-FTL.log)"
# Extract relevant log lines
run bash -c "sed -n \"${before},${after}p\" /var/log/pihole-FTL.log"
printf "%s\n" "${lines[@]}"
[[ "${lines[@]}" == *"EDNS(0) CLIENT SUBNET: Skipped 127.0.0.1/32 (IPv4 loopback address)"* ]]
}
@test "EDNS(0) ECS skipped for loopback address (IPv6)" {
# Get number of lines in the log before the test
before="$(grep -c ^ /var/log/pihole-FTL.log)"
# Run test command
run bash -c 'dig localhost +short +subnet=::1/128 @127.0.0.1'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "127.0.0.1" ]]
[[ $status == 0 ]]
# Get number of lines in the log after the test
after="$(grep -c ^ /var/log/pihole-FTL.log)"
# Extract relevant log lines
run bash -c "sed -n \"${before},${after}p\" /var/log/pihole-FTL.log"
printf "%s\n" "${lines[@]}"
[[ "${lines[@]}" == *"EDNS(0) CLIENT SUBNET: Skipped ::1/128 (IPv6 loopback address)"* ]]
}