Merge branch 'development-v6' into new/validator
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
commit
9b18127867
|
@ -119,7 +119,7 @@ jobs:
|
|||
-
|
||||
name: Store binary artifacts for later deployoment
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4.2.0
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
with:
|
||||
name: ${{ matrix.bin_name }}-binary
|
||||
path: '${{ matrix.bin_name }}*'
|
||||
|
@ -131,7 +131,7 @@ jobs:
|
|||
-
|
||||
name: Upload documentation artifacts for deployoment
|
||||
if: github.event_name != 'pull_request' && matrix.platform == 'linux/amd64'
|
||||
uses: actions/upload-artifact@v4.2.0
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
with:
|
||||
name: pihole-api-docs
|
||||
path: 'api-docs.tar.gz'
|
||||
|
|
|
@ -294,7 +294,7 @@ find_library(LIBGMP NAMES libgmp${CMAKE_STATIC_LIBRARY_SUFFIX} gmp)
|
|||
find_library(LIBNETTLE NAMES libnettle${CMAKE_STATIC_LIBRARY_SUFFIX} nettle HINTS /usr/local/lib64)
|
||||
|
||||
# for IDN2 we need the idn2 library which in turn depends on the unistring library
|
||||
find_library(LIBIDN2 NAMES libidn2${CMAKE_STATIC_LIBRARY_SUFFIX} idn)
|
||||
find_library(LIBIDN2 NAMES libidn2${CMAKE_STATIC_LIBRARY_SUFFIX} idn2)
|
||||
find_library(LIBUNISTRING NAMES libunistring${CMAKE_STATIC_LIBRARY_SUFFIX} unistring)
|
||||
|
||||
target_link_libraries(pihole-FTL rt Threads::Threads ${LIBHOGWEED} ${LIBGMP} ${LIBNETTLE} ${LIBIDN2} ${LIBUNISTRING})
|
||||
|
|
|
@ -76,7 +76,7 @@ components:
|
|||
properties:
|
||||
expires:
|
||||
type: integer
|
||||
description: Expiration time
|
||||
description: Expiration time (0 = infinite lease, never expires)
|
||||
example: 1675671991
|
||||
name:
|
||||
type: string
|
||||
|
@ -112,4 +112,4 @@ components:
|
|||
type: string
|
||||
required: true
|
||||
description: IP address of lease to be modified
|
||||
example: 192.168.2.222
|
||||
example: 192.168.2.222
|
||||
|
|
|
@ -216,9 +216,9 @@ components:
|
|||
time:
|
||||
type: number
|
||||
description: Time until the response was received (ms, negative if N/A)
|
||||
regex_id:
|
||||
list_id:
|
||||
type: integer
|
||||
description: ID of regex (`NULL` if N/A)
|
||||
description: ID of corresponding database table (adlist for anti-/gravity, else domainlist) (`NULL` if N/A)
|
||||
nullable: true
|
||||
upstream:
|
||||
type: string
|
||||
|
@ -237,7 +237,7 @@ components:
|
|||
reply:
|
||||
type: "IP"
|
||||
time: 19
|
||||
regex_id: NULL
|
||||
list_id: NULL
|
||||
upstream: "localhost#5353"
|
||||
dbid: 112421354
|
||||
- time: 1581907871.583821
|
||||
|
@ -252,7 +252,7 @@ components:
|
|||
reply:
|
||||
type: "IP"
|
||||
time: 12.3
|
||||
regex_id: NULL
|
||||
list_id: NULL
|
||||
upstream: "localhost#5353"
|
||||
dbid: 112421355
|
||||
cursor:
|
||||
|
|
|
@ -173,7 +173,7 @@ int api_queries_suggestions(struct ftl_conn *api)
|
|||
JSON_SEND_OBJECT(json);
|
||||
}
|
||||
|
||||
#define QUERYSTR "SELECT q.id,timestamp,q.type,status,d.domain,f.forward,additional_info,reply_type,reply_time,dnssec,c.ip,c.name,a.content,regex_id"
|
||||
#define QUERYSTR "SELECT q.id,timestamp,q.type,status,d.domain,f.forward,additional_info,reply_type,reply_time,dnssec,c.ip,c.name,a.content,list_id"
|
||||
// JOIN: Only return rows where there is a match in BOTH tables
|
||||
// LEFT JOIN: Return all rows from the left table, and the matched rows from the right table
|
||||
#define JOINSTR "JOIN client_by_id c ON q.client = c.id JOIN domain_by_id d ON q.domain = d.id LEFT JOIN forward_by_id f ON q.forward = f.id LEFT JOIN addinfo_by_id a ON a.id = q.additional_info"
|
||||
|
@ -214,8 +214,8 @@ static void querystr_finish(char *querystr, const char *sort_col, const char *so
|
|||
sort_col_sql = "q.reply_time";
|
||||
else if(strcasecmp(sort_col, "dnssec") == 0)
|
||||
sort_col_sql = "q.dnssec";
|
||||
else if(strcasecmp(sort_col, "regex_id") == 0)
|
||||
sort_col_sql = "regex_id";
|
||||
else if(strcasecmp(sort_col, "list_id") == 0)
|
||||
sort_col_sql = "list_id";
|
||||
|
||||
// ... and the sort direction
|
||||
if(strcasecmp(sort_dir, "asc") == 0 || strcasecmp(sort_dir, "ascending") == 0)
|
||||
|
@ -887,11 +887,11 @@ int api_queries(struct ftl_conn *api)
|
|||
JSON_ADD_NULL_TO_OBJECT(client, "name");
|
||||
JSON_ADD_ITEM_TO_OBJECT(item, "client", client);
|
||||
|
||||
// Add regex_id if it exists
|
||||
// Add list_id if it exists
|
||||
if(sqlite3_column_type(read_stmt, 13) == SQLITE_INTEGER)
|
||||
JSON_ADD_NUMBER_TO_OBJECT(item, "regex_id", sqlite3_column_int(read_stmt, 13)); // regex_id
|
||||
JSON_ADD_NUMBER_TO_OBJECT(item, "list_id", sqlite3_column_int(read_stmt, 13)); // list_id
|
||||
else
|
||||
JSON_ADD_NULL_TO_OBJECT(item, "regex_id");
|
||||
JSON_ADD_NULL_TO_OBJECT(item, "list_id");
|
||||
|
||||
const unsigned char *cname = NULL;
|
||||
switch(query.status)
|
||||
|
|
22
src/args.c
22
src/args.c
|
@ -64,6 +64,8 @@
|
|||
#include <idn2.h>
|
||||
// sha256sum()
|
||||
#include "files.h"
|
||||
// resolveHostname()
|
||||
#include "resolve.h"
|
||||
|
||||
// defined in dnsmasq.c
|
||||
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);
|
||||
|
@ -496,6 +498,25 @@ void parse_args(int argc, char* argv[])
|
|||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Local reverse name resolver
|
||||
if(argc == 3 && strcasecmp(argv[1], "ptr") == 0)
|
||||
{
|
||||
// Enable stdout printing
|
||||
cli_mode = true;
|
||||
|
||||
// Need to get dns.port and the resolver settings
|
||||
readFTLconf(&config, false);
|
||||
|
||||
char *name = resolveHostname(argv[2], true);
|
||||
if(name == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
// Print result
|
||||
printf("%s\n", name);
|
||||
free(name);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// start from 1, as argv[0] is the executable name
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
|
@ -980,6 +1001,7 @@ void parse_args(int argc, char* argv[])
|
|||
printf(" Decoding: %spihole-FTL idn2 -d %spunycode%s\n\n", green, cyan, normal);
|
||||
|
||||
printf("%sOther:%s\n", yellow, normal);
|
||||
printf("\t%sptr %sIP%s Resolve IP address to hostname\n", green, cyan, normal);
|
||||
printf("\t%ssha256sum %sfile%s Calculate SHA256 checksum of a file\n", green, cyan, normal);
|
||||
printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal);
|
||||
printf("\t network\n");
|
||||
|
|
|
@ -438,8 +438,8 @@ void initConfig(struct config *conf)
|
|||
struct enum_options piholePTR[] =
|
||||
{
|
||||
{ get_ptr_type_str(PTR_NONE), "Pi-hole will not respond automatically on PTR requests to local interface addresses. Ensure pi.hole and/or hostname records exist elsewhere." },
|
||||
{ get_ptr_type_str(PTR_HOSTNAME), "Pi-hole will not respond automatically on PTR requests to local interface addresses. Ensure pi.hole and/or hostname records exist elsewhere." },
|
||||
{ get_ptr_type_str(PTR_HOSTNAMEFQDN), "Serve the machine's global hostname as fully qualified domain by adding the local suffix. If no local suffix has been defined, FTL appends the local domain .no_fqdn_available. In this case you should either add domain=whatever.com to a custom config file inside /etc/dnsmasq.d/ (to set whatever.com as local domain) or use domain=# which will try to derive the local domain from /etc/resolv.conf (or whatever is set with resolv-file, when multiple search directives exist, the first one is used)." },
|
||||
{ get_ptr_type_str(PTR_HOSTNAME), "Serve the machine's hostname. The hostname is queried from the kernel through uname(2)->nodename. If the machine has multiple network interfaces, it can also have multiple nodenames. In this case, it is unspecified and up to the kernel which one will be returned. On Linux, the returned string is what has been set using sethostname(2) which is typically what has been set in /etc/hostname." },
|
||||
{ get_ptr_type_str(PTR_HOSTNAMEFQDN), "Serve the machine's hostname (see limitations above) as fully qualified domain by adding the local domain. If no local domain has been defined (config option dns.domain), FTL tries to query the domain name from the kernel using getdomainname(2). If this fails, FTL appends \".no_fqdn_available\" to the hostname." },
|
||||
{ get_ptr_type_str(PTR_PIHOLE), "Respond with \"pi.hole\"." }
|
||||
};
|
||||
CONFIG_ADD_ENUM_OPTIONS(conf->dns.piholePTR.a, piholePTR);
|
||||
|
@ -514,8 +514,8 @@ void initConfig(struct config *conf)
|
|||
conf->dns.dnssec.h = "Validate DNS replies using DNSSEC?";
|
||||
conf->dns.dnssec.t = CONF_BOOL;
|
||||
conf->dns.dnssec.f = FLAG_RESTART_FTL;
|
||||
conf->dns.dnssec.d.b = true;
|
||||
conf->dns.dnssec.c = validate_stub; // Only type-based checking
|
||||
conf->dns.dnssec.d.b = false;
|
||||
|
||||
conf->dns.interface.k = "dns.interface";
|
||||
conf->dns.interface.h = "Interface to use for DNS (see also dnsmasq.listening.mode) and DHCP (if enabled)";
|
||||
|
|
23
src/daemon.c
23
src/daemon.c
|
@ -178,20 +178,39 @@ char *getUserName(void)
|
|||
// hyphen.
|
||||
#define HOSTNAMESIZE 256
|
||||
static char nodename[HOSTNAMESIZE] = { 0 };
|
||||
static char dname[HOSTNAMESIZE] = { 0 };
|
||||
|
||||
// Returns the hostname of the system
|
||||
const char *hostname(void)
|
||||
{
|
||||
// Ask kernel for node name if not known
|
||||
// This is equivalent to "uname -n"
|
||||
//
|
||||
// According to man gethostname(2), this is exactly the same as calling
|
||||
// getdomainname() just with one step less
|
||||
if(nodename[0] == '\0')
|
||||
{
|
||||
struct utsname buf;
|
||||
if(uname(&buf) == 0)
|
||||
{
|
||||
strncpy(nodename, buf.nodename, HOSTNAMESIZE);
|
||||
nodename[HOSTNAMESIZE-1] = '\0';
|
||||
strncpy(dname, buf.domainname, HOSTNAMESIZE);
|
||||
}
|
||||
nodename[HOSTNAMESIZE - 1] = '\0';
|
||||
dname[HOSTNAMESIZE - 1] = '\0';
|
||||
}
|
||||
return nodename;
|
||||
}
|
||||
|
||||
// Returns the domain name of the system
|
||||
const char *domainname(void)
|
||||
{
|
||||
if(dname[0] == '\0')
|
||||
hostname();
|
||||
|
||||
return dname;
|
||||
}
|
||||
|
||||
void delay_startup(void)
|
||||
{
|
||||
// Exit early if not sleeping
|
||||
|
@ -463,4 +482,4 @@ void init_locale(void)
|
|||
// the dot as decimal separator (even if the system locale uses a
|
||||
// comma, e.g., in German)
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ void go_daemon(void);
|
|||
void savepid(void);
|
||||
char *getUserName(void);
|
||||
const char *hostname(void);
|
||||
const char *domainname(void);
|
||||
void delay_startup(void);
|
||||
bool is_fork(const pid_t mpid, const pid_t pid) __attribute__ ((const));
|
||||
void cleanup(const int ret);
|
||||
|
|
|
@ -549,6 +549,26 @@ void db_init(void)
|
|||
dbversion = db_get_int(db, DB_VERSION);
|
||||
}
|
||||
|
||||
// Update to version 17 if lower
|
||||
if(dbversion < 17)
|
||||
{
|
||||
// Update to version 17: Rename regex_id column to regex_id_old
|
||||
log_info("Updating long-term database to version 17");
|
||||
if(!rename_query_storage_column_regex_id(db))
|
||||
{
|
||||
log_info("regex_id cannot be renamed to list_id, database not available");
|
||||
dbclose(&db);
|
||||
return;
|
||||
}
|
||||
// Get updated version
|
||||
dbversion = db_get_int(db, DB_VERSION);
|
||||
}
|
||||
|
||||
// Last check after all migrations, if this happens, it will cause the
|
||||
// CI to fail the tests
|
||||
if(dbversion != MEMDB_VERSION)
|
||||
log_err("Database version %i does not match MEMDB_VERSION %i", dbversion, MEMDB_VERSION);
|
||||
|
||||
lock_shm();
|
||||
import_aliasclients(db);
|
||||
unlock_shm();
|
||||
|
|
|
@ -857,7 +857,7 @@ bool gravityDB_prepare_client_statements(clientsData *client)
|
|||
|
||||
// Prepare gravity statement
|
||||
log_debug(DEBUG_DATABASE, "gravityDB_open(): Preparing vw_gravity statement for client %s", clientip);
|
||||
querystr = get_client_querystr("vw_gravity", "domain", getstr(client->groupspos));
|
||||
querystr = get_client_querystr("vw_gravity", "adlist_id", getstr(client->groupspos));
|
||||
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
|
@ -870,7 +870,7 @@ bool gravityDB_prepare_client_statements(clientsData *client)
|
|||
|
||||
// Prepare antigravity statement
|
||||
log_debug(DEBUG_DATABASE, "gravityDB_open(): Preparing vw_antigravity statement for client %s", clientip);
|
||||
querystr = get_client_querystr("vw_antigravity", "domain", getstr(client->groupspos));
|
||||
querystr = get_client_querystr("vw_antigravity", "adlist_id", getstr(client->groupspos));
|
||||
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
|
@ -1244,6 +1244,11 @@ enum db_result in_allowlist(const char *domain, DNSCacheData *dns_cache, clients
|
|||
// Check if this client needs a rechecking of group membership
|
||||
gravityDB_client_check_again(client);
|
||||
|
||||
// Check again as the client may have been reloaded if this is a TCP
|
||||
// worker
|
||||
if(whitelist_stmt == NULL)
|
||||
return LIST_NOT_AVAILABLE;
|
||||
|
||||
// Get whitelist statement from vector of prepared statements if available
|
||||
sqlite3_stmt *stmt = whitelist_stmt->get(whitelist_stmt, client->id);
|
||||
|
||||
|
@ -1262,7 +1267,7 @@ enum db_result in_allowlist(const char *domain, DNSCacheData *dns_cache, clients
|
|||
// We have to check both the exact whitelist (using a prepared database statement)
|
||||
// as well the compiled regex whitelist filters to check if the current domain is
|
||||
// whitelisted.
|
||||
return domain_in_list(domain, stmt, "whitelist", &dns_cache->domainlist_id);
|
||||
return domain_in_list(domain, stmt, "whitelist", &dns_cache->list_id);
|
||||
}
|
||||
|
||||
cJSON *gen_abp_patterns(const char *domain, const bool antigravity)
|
||||
|
@ -1377,6 +1382,11 @@ enum db_result in_gravity(const char *domain, clientsData *client, const bool an
|
|||
// Check if this client needs a rechecking of group membership
|
||||
gravityDB_client_check_again(client);
|
||||
|
||||
// Check again as the client may have been reloaded if this is a TCP
|
||||
// worker
|
||||
if(gravity_stmt == NULL || antigravity_stmt == NULL)
|
||||
return LIST_NOT_AVAILABLE;
|
||||
|
||||
// Get whitelist statement from vector of prepared statements
|
||||
sqlite3_stmt *stmt = antigravity ?
|
||||
antigravity_stmt->get(antigravity_stmt, client->id) :
|
||||
|
@ -1451,6 +1461,11 @@ enum db_result in_denylist(const char *domain, DNSCacheData *dns_cache, clientsD
|
|||
// Check if this client needs a rechecking of group membership
|
||||
gravityDB_client_check_again(client);
|
||||
|
||||
// Check again as the client may have been reloaded if this is a TCP
|
||||
// worker
|
||||
if(blacklist_stmt == NULL)
|
||||
return LIST_NOT_AVAILABLE;
|
||||
|
||||
// Get whitelist statement from vector of prepared statements
|
||||
sqlite3_stmt *stmt = blacklist_stmt->get(blacklist_stmt, client->id);
|
||||
|
||||
|
@ -1466,7 +1481,7 @@ enum db_result in_denylist(const char *domain, DNSCacheData *dns_cache, clientsD
|
|||
if(stmt == NULL)
|
||||
stmt = blacklist_stmt->get(blacklist_stmt, client->id);
|
||||
|
||||
return domain_in_list(domain, stmt, "blacklist", &dns_cache->domainlist_id);
|
||||
return domain_in_list(domain, stmt, "blacklist", &dns_cache->list_id);
|
||||
}
|
||||
|
||||
bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex,
|
||||
|
@ -1969,8 +1984,8 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
else if(listtype == GRAVITY_ADLISTS)
|
||||
{
|
||||
// This is actually a three-step deletion to satisfy foreign-key constraints
|
||||
querystrs[0] = "DELETE FROM gravity WHERE adlist_id = (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id = (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[0] = "DELETE FROM gravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[2] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable);";
|
||||
}
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
|
@ -1983,7 +1998,6 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
querystrs[3] = "DELETE FROM domainlist WHERE domain IN (SELECT item FROM deltable WHERE type = 3) AND type = 3;";
|
||||
}
|
||||
|
||||
bool okay = true;
|
||||
for(unsigned int i = 0; i < ArraySize(querystrs); i++)
|
||||
{
|
||||
// Finish if no more queries
|
||||
|
@ -1997,13 +2011,12 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
*message = sqlite3_errmsg(gravity_db);
|
||||
log_err("gravityDB_delFromTable(%d): SQL error exec(\"%s\"): %s",
|
||||
listtype, querystrs[i], *message);
|
||||
okay = false;
|
||||
|
||||
// Rollback transaction
|
||||
querystr = "ROLLBACK TRANSACTION;";
|
||||
sqlite3_exec(gravity_db, querystr, NULL, NULL, NULL);
|
||||
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add number of deleted rows
|
||||
|
@ -2018,7 +2031,6 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
*message = sqlite3_errmsg(gravity_db);
|
||||
log_err("gravityDB_delFromTable(%d): SQL error exec(\"%s\"): %s",
|
||||
listtype, querystr, *message);
|
||||
okay = false;
|
||||
|
||||
// Rollback transaction
|
||||
querystr = "ROLLBACK TRANSACTION;";
|
||||
|
@ -2033,14 +2045,13 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
*message = sqlite3_errmsg(gravity_db);
|
||||
log_err("gravityDB_delFromTable(%d): SQL error exec(\"%s\"): %s",
|
||||
listtype, querystr, *message);
|
||||
okay = false;
|
||||
|
||||
// Rollback transaction
|
||||
querystr = "ROLLBACK TRANSACTION;";
|
||||
sqlite3_exec(gravity_db, querystr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
return okay;
|
||||
return true;
|
||||
}
|
||||
|
||||
static sqlite3_stmt* read_stmt = NULL;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "../datastructure.h"
|
||||
// struct config
|
||||
#include "../config/config.h"
|
||||
// resolveHostname()
|
||||
// resolve_this_name()
|
||||
#include "../resolve.h"
|
||||
// killed
|
||||
#include "../signals.h"
|
||||
|
|
|
@ -770,6 +770,29 @@ bool add_ftl_table_description(sqlite3 *db)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool rename_query_storage_column_regex_id(sqlite3 *db)
|
||||
{
|
||||
// Start transaction of database update
|
||||
SQL_bool(db, "BEGIN TRANSACTION");
|
||||
|
||||
// Rename column regex_id to list_id
|
||||
SQL_bool(db, "ALTER TABLE query_storage RENAME COLUMN regex_id TO list_id;");
|
||||
|
||||
// The VIEW queries is automatically updated by SQLite3
|
||||
|
||||
// Update database version to 17
|
||||
if(!db_set_FTL_property(db, DB_VERSION, 17))
|
||||
{
|
||||
log_err("rename_query_storage_column_regex_id(): Failed to update database version!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish transaction
|
||||
SQL_bool(db, "COMMIT");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optimize_queries_table(sqlite3 *db)
|
||||
{
|
||||
// Start transaction of database update
|
||||
|
@ -1118,7 +1141,7 @@ void DB_read_queries(void)
|
|||
// a) we have a cache entry
|
||||
// b) the value of additional_info is not NULL (0 bytes storage size)
|
||||
if(cache != NULL && sqlite3_column_bytes(stmt, 7) != 0)
|
||||
cache->domainlist_id = sqlite3_column_int(stmt, 7);
|
||||
cache->list_id = sqlite3_column_int(stmt, 7);
|
||||
}
|
||||
|
||||
// Increment status counters
|
||||
|
@ -1467,15 +1490,15 @@ bool queries_to_database(void)
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if(cache != NULL && query->status == QUERY_REGEX)
|
||||
else if(cache != NULL && cache->list_id != -1)
|
||||
{
|
||||
// Restore regex ID if applicable
|
||||
sqlite3_bind_int(query_stmt, 9, ADDINFO_REGEX_ID);
|
||||
sqlite3_bind_int(query_stmt, 10, cache->domainlist_id);
|
||||
sqlite3_bind_int(query_stmt, 9, ADDINFO_LIST_ID);
|
||||
sqlite3_bind_int(query_stmt, 10, cache->list_id);
|
||||
|
||||
// Execute prepared addinfo statement and check if successful
|
||||
sqlite3_bind_int(addinfo_stmt, 1, ADDINFO_REGEX_ID);
|
||||
sqlite3_bind_int(addinfo_stmt, 2, cache->domainlist_id);
|
||||
sqlite3_bind_int(addinfo_stmt, 1, ADDINFO_LIST_ID);
|
||||
sqlite3_bind_int(addinfo_stmt, 2, cache->list_id);
|
||||
rc = sqlite3_step(addinfo_stmt);
|
||||
sqlite3_clear_bindings(addinfo_stmt);
|
||||
sqlite3_reset(addinfo_stmt);
|
||||
|
@ -1506,9 +1529,9 @@ bool queries_to_database(void)
|
|||
// DNSSEC
|
||||
sqlite3_bind_int(query_stmt, 13, query->dnssec);
|
||||
|
||||
// REGEX_ID
|
||||
if(cache != NULL && cache->domainlist_id > -1)
|
||||
sqlite3_bind_int(query_stmt, 14, cache->domainlist_id);
|
||||
// LIST_ID
|
||||
if(cache != NULL && cache->list_id != -1)
|
||||
sqlite3_bind_int(query_stmt, 14, cache->list_id);
|
||||
else
|
||||
// Not applicable, setting NULL
|
||||
sqlite3_bind_null(query_stmt, 14);
|
||||
|
|
|
@ -23,20 +23,21 @@
|
|||
"client TEXT NOT NULL, " \
|
||||
"forward TEXT );"
|
||||
|
||||
#define CREATE_QUERY_STORAGE_TABLE_V13 "CREATE TABLE query_storage ( id INTEGER PRIMARY KEY AUTOINCREMENT, " \
|
||||
"timestamp INTEGER NOT NULL, " \
|
||||
"type INTEGER NOT NULL, " \
|
||||
"status INTEGER NOT NULL, " \
|
||||
"domain INTEGER NOT NULL, " \
|
||||
"client INTEGER NOT NULL, " \
|
||||
"forward INTEGER, " \
|
||||
"additional_info INTEGER, " \
|
||||
"reply_type INTEGER, " \
|
||||
"reply_time REAL, " \
|
||||
"dnssec INTEGER, " \
|
||||
"regex_id INTEGER );"
|
||||
#define MEMDB_VERSION 17
|
||||
#define CREATE_QUERY_STORAGE_TABLE "CREATE TABLE query_storage ( id INTEGER PRIMARY KEY AUTOINCREMENT, " \
|
||||
"timestamp INTEGER NOT NULL, " \
|
||||
"type INTEGER NOT NULL, " \
|
||||
"status INTEGER NOT NULL, " \
|
||||
"domain INTEGER NOT NULL, " \
|
||||
"client INTEGER NOT NULL, " \
|
||||
"forward INTEGER, " \
|
||||
"additional_info INTEGER, " \
|
||||
"reply_type INTEGER, " \
|
||||
"reply_time REAL, " \
|
||||
"dnssec INTEGER, " \
|
||||
"list_id INTEGER );"
|
||||
|
||||
#define CREATE_QUERIES_VIEW_V13 "CREATE VIEW queries AS " \
|
||||
#define CREATE_QUERIES_VIEW "CREATE VIEW queries AS " \
|
||||
"SELECT id, timestamp, type, status, " \
|
||||
"CASE typeof(domain) " \
|
||||
"WHEN 'integer' THEN (SELECT domain FROM domain_by_id d WHERE d.id = q.domain) ELSE domain END domain," \
|
||||
|
@ -46,7 +47,7 @@
|
|||
"WHEN 'integer' THEN (SELECT forward FROM forward_by_id f WHERE f.id = q.forward) ELSE forward END forward," \
|
||||
"CASE typeof(additional_info) "\
|
||||
"WHEN 'integer' THEN (SELECT content FROM addinfo_by_id a WHERE a.id = q.additional_info) ELSE additional_info END additional_info, " \
|
||||
"reply_type, reply_time, dnssec, regex_id FROM query_storage q"
|
||||
"reply_type, reply_time, dnssec, list_id FROM query_storage q"
|
||||
|
||||
// Version 1
|
||||
#define CREATE_QUERIES_TIMESTAMP_INDEX "CREATE INDEX idx_queries_timestamp ON queries (timestamp);"
|
||||
|
@ -62,8 +63,7 @@
|
|||
#define CREATE_QUERY_STORAGE_REPLY_TYPE_INDEX "CREATE INDEX idx_query_storage_reply_type ON query_storage (reply_type);"
|
||||
#define CREATE_QUERY_STORAGE_REPLY_TIME_INDEX "CREATE INDEX idx_query_storage_reply_time ON query_storage (reply_time);"
|
||||
#define CREATE_QUERY_STORAGE_DNSSEC_INDEX "CREATE INDEX idx_query_storage_dnssec ON query_storage (dnssec);"
|
||||
//#define CREATE_QUERY_STORAGE_TTL_INDEX "CREATE INDEX idx_query_storage_ttl ON query_storage (ttl);"
|
||||
//#define CREATE_QUERY_STORAGE_REGEX_ID_INDEX "CREATE INDEX idx_query_storage_regex_id ON query_storage (regex_id);"
|
||||
#define CREATE_QUERY_STORAGE_LIST_ID_INDEX "CREATE INDEX idx_query_storage_list_id ON query_storage (list_id);"
|
||||
|
||||
#define CREATE_DOMAINS_BY_ID "CREATE TABLE domain_by_id (id INTEGER PRIMARY KEY, domain TEXT NOT NULL);"
|
||||
#define CREATE_CLIENTS_BY_ID "CREATE TABLE client_by_id (id INTEGER PRIMARY KEY, ip TEXT NOT NULL, name TEXT);"
|
||||
|
@ -77,12 +77,12 @@
|
|||
|
||||
#ifdef QUERY_TABLE_PRIVATE
|
||||
const char *table_creation[] = {
|
||||
CREATE_QUERY_STORAGE_TABLE_V13,
|
||||
CREATE_QUERY_STORAGE_TABLE,
|
||||
CREATE_DOMAINS_BY_ID,
|
||||
CREATE_CLIENTS_BY_ID,
|
||||
CREATE_FORWARD_BY_ID,
|
||||
CREATE_ADDINFO_BY_ID,
|
||||
CREATE_QUERIES_VIEW_V13,
|
||||
CREATE_QUERIES_VIEW,
|
||||
};
|
||||
const char *index_creation[] = {
|
||||
CREATE_QUERY_STORAGE_ID_INDEX,
|
||||
|
@ -96,8 +96,7 @@ const char *index_creation[] = {
|
|||
CREATE_QUERY_STORAGE_REPLY_TYPE_INDEX,
|
||||
CREATE_QUERY_STORAGE_REPLY_TIME_INDEX,
|
||||
CREATE_QUERY_STORAGE_DNSSEC_INDEX,
|
||||
// CREATE_QUERY_STORAGE_TTL_INDEX,
|
||||
// CREATE_QUERY_STORAGE_REGEX_ID_INDEX
|
||||
CREATE_QUERY_STORAGE_LIST_ID_INDEX
|
||||
CREATE_DOMAIN_BY_ID_DOMAIN_INDEX,
|
||||
CREATE_CLIENTS_BY_ID_IPNAME_INDEX,
|
||||
CREATE_FORWARD_BY_ID_FORWARD_INDEX,
|
||||
|
@ -126,5 +125,6 @@ bool create_addinfo_table(sqlite3 *db);
|
|||
bool add_query_storage_columns(sqlite3 *db);
|
||||
bool add_query_storage_column_regex_id(sqlite3 *db);
|
||||
bool add_ftl_table_description(sqlite3 *db);
|
||||
bool rename_query_storage_column_regex_id(sqlite3 *db);
|
||||
|
||||
#endif //QUERY_TABLE_PRIVATE_H
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
** This file is an amalgamation of many separate C source files from SQLite
|
||||
** version 3.45.0. By combining all the individual C code files into this
|
||||
** version 3.45.1. By combining all the individual C code files into this
|
||||
** single large file, the entire code can be compiled as a single translation
|
||||
** unit. This allows many compilers to do optimizations that would not be
|
||||
** possible if the files were compiled separately. Performance improvements
|
||||
|
@ -18,7 +18,7 @@
|
|||
** separate file. This file contains only code for the core SQLite library.
|
||||
**
|
||||
** The content in this amalgamation comes from Fossil check-in
|
||||
** 1066602b2b1976fe58b5150777cced894af1.
|
||||
** e876e51a0ed5c5b3126f52e532044363a014.
|
||||
*/
|
||||
#define SQLITE_CORE 1
|
||||
#define SQLITE_AMALGAMATION 1
|
||||
|
@ -459,9 +459,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.45.0"
|
||||
#define SQLITE_VERSION_NUMBER 3045000
|
||||
#define SQLITE_SOURCE_ID "2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d"
|
||||
#define SQLITE_VERSION "3.45.1"
|
||||
#define SQLITE_VERSION_NUMBER 3045001
|
||||
#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -43408,11 +43408,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
|||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
if( pFd->mmapSizeMax>0 ){
|
||||
/* Ensure that there is always at least a 256 byte buffer of addressable
|
||||
** memory following the returned page. If the database is corrupt,
|
||||
** SQLite may overread the page slightly (in practice only a few bytes,
|
||||
** but 256 is safe, round, number). */
|
||||
const int nEofBuffer = 256;
|
||||
if( pFd->pMapRegion==0 ){
|
||||
int rc = unixMapfile(pFd, -1);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
if( pFd->mmapSize >= iOff+nAmt ){
|
||||
if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
|
||||
*pp = &((u8 *)pFd->pMapRegion)[iOff];
|
||||
pFd->nFetchOut++;
|
||||
}
|
||||
|
@ -50765,6 +50770,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
|||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
if( pFd->mmapSizeMax>0 ){
|
||||
/* Ensure that there is always at least a 256 byte buffer of addressable
|
||||
** memory following the returned page. If the database is corrupt,
|
||||
** SQLite may overread the page slightly (in practice only a few bytes,
|
||||
** but 256 is safe, round, number). */
|
||||
const int nEofBuffer = 256;
|
||||
if( pFd->pMapRegion==0 ){
|
||||
int rc = winMapfile(pFd, -1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -50773,7 +50783,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
|||
return rc;
|
||||
}
|
||||
}
|
||||
if( pFd->mmapSize >= iOff+nAmt ){
|
||||
if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
|
||||
assert( pFd->pMapRegion!=0 );
|
||||
*pp = &((u8 *)pFd->pMapRegion)[iOff];
|
||||
pFd->nFetchOut++;
|
||||
|
@ -76402,7 +76412,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
|
|||
}
|
||||
|
||||
pPage = pCur->pPage;
|
||||
assert( pPage->isInit );
|
||||
if( sqlite3FaultSim(412) ) pPage->isInit = 0;
|
||||
if( !pPage->isInit ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( !pPage->leaf ){
|
||||
int idx = pCur->ix;
|
||||
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
|
||||
|
@ -166812,7 +166825,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
|
|||
|
||||
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
|
||||
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
|
||||
if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
|
||||
if( pOrderBy && pOrderBy->nExpr>=BMS ){
|
||||
pOrderBy = 0;
|
||||
wctrlFlags &= ~WHERE_WANT_DISTINCT;
|
||||
}
|
||||
|
||||
/* The number of tables in the FROM clause is limited by the number of
|
||||
** bits in a Bitmask
|
||||
|
@ -184747,6 +184763,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
|
|||
|
||||
SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);
|
||||
|
||||
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
|
||||
|
||||
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
|
||||
#endif /* _FTSINT_H */
|
||||
|
||||
|
@ -188469,7 +188487,7 @@ static int fts3ShadowName(const char *zName){
|
|||
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
|
||||
** table.
|
||||
*/
|
||||
static int fts3Integrity(
|
||||
static int fts3IntegrityMethod(
|
||||
sqlite3_vtab *pVtab, /* The virtual table to be checked */
|
||||
const char *zSchema, /* Name of schema in which pVtab lives */
|
||||
const char *zTabname, /* Name of the pVTab table */
|
||||
|
@ -188477,30 +188495,21 @@ static int fts3Integrity(
|
|||
char **pzErr /* Write error message here */
|
||||
){
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
char *zSql;
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
int bOk = 0;
|
||||
|
||||
assert( pzErr!=0 );
|
||||
assert( *pzErr==0 );
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
zSchema, zTabname, zTabname);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, &zErr);
|
||||
sqlite3_free(zSql);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname);
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
|
||||
assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS%d table %s.%s: %s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname, zErr);
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}else if( bOk==0 ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname);
|
||||
}
|
||||
sqlite3_free(zErr);
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -188531,7 +188540,7 @@ static const sqlite3_module fts3Module = {
|
|||
/* xRelease */ fts3ReleaseMethod,
|
||||
/* xRollbackTo */ fts3RollbackToMethod,
|
||||
/* xShadowName */ fts3ShadowName,
|
||||
/* xIntegrity */ fts3Integrity,
|
||||
/* xIntegrity */ fts3IntegrityMethod,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -200085,7 +200094,7 @@ static u64 fts3ChecksumIndex(
|
|||
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
|
||||
** code. The final value of *pbOk is undefined in this case.
|
||||
*/
|
||||
static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
u64 cksum1 = 0; /* Checksum based on FTS index contents */
|
||||
u64 cksum2 = 0; /* Checksum based on %_content contents */
|
||||
|
@ -200163,7 +200172,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
|||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
*pbOk = (cksum1==cksum2);
|
||||
*pbOk = (rc==SQLITE_OK && cksum1==cksum2);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -200203,7 +200212,7 @@ static int fts3DoIntegrityCheck(
|
|||
){
|
||||
int rc;
|
||||
int bOk = 0;
|
||||
rc = fts3IntegrityCheck(p, &bOk);
|
||||
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
|
||||
if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
|
||||
return rc;
|
||||
}
|
||||
|
@ -203756,6 +203765,16 @@ static void jsonAppendChar(JsonString *p, char c){
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove a single character from the end of the string
|
||||
*/
|
||||
static void jsonStringTrimOneChar(JsonString *p){
|
||||
if( p->eErr==0 ){
|
||||
assert( p->nUsed>0 );
|
||||
p->nUsed--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Make sure there is a zero terminator on p->zBuf[]
|
||||
**
|
||||
** Return true on success. Return false if an OOM prevents this
|
||||
|
@ -203763,7 +203782,7 @@ static void jsonAppendChar(JsonString *p, char c){
|
|||
*/
|
||||
static int jsonStringTerminate(JsonString *p){
|
||||
jsonAppendChar(p, 0);
|
||||
p->nUsed--;
|
||||
jsonStringTrimOneChar(p);
|
||||
return p->eErr==0;
|
||||
}
|
||||
|
||||
|
@ -205229,8 +205248,8 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
|
|||
(pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
|
||||
n = 9;
|
||||
}
|
||||
if( i+sz+n > pParse->nBlob
|
||||
&& i+sz+n > pParse->nBlob-pParse->delta
|
||||
if( (i64)i+sz+n > pParse->nBlob
|
||||
&& (i64)i+sz+n > pParse->nBlob-pParse->delta
|
||||
){
|
||||
sz = 0;
|
||||
n = 0;
|
||||
|
@ -205280,6 +205299,7 @@ static u32 jsonTranslateBlobToText(
|
|||
}
|
||||
case JSONB_INT:
|
||||
case JSONB_FLOAT: {
|
||||
if( sz==0 ) goto malformed_jsonb;
|
||||
jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
|
||||
break;
|
||||
}
|
||||
|
@ -205288,6 +205308,7 @@ static u32 jsonTranslateBlobToText(
|
|||
sqlite3_uint64 u = 0;
|
||||
const char *zIn = (const char*)&pParse->aBlob[i+n];
|
||||
int bOverflow = 0;
|
||||
if( sz==0 ) goto malformed_jsonb;
|
||||
if( zIn[0]=='-' ){
|
||||
jsonAppendChar(pOut, '-');
|
||||
k++;
|
||||
|
@ -205310,6 +205331,7 @@ static u32 jsonTranslateBlobToText(
|
|||
case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
|
||||
u32 k = 0;
|
||||
const char *zIn = (const char*)&pParse->aBlob[i+n];
|
||||
if( sz==0 ) goto malformed_jsonb;
|
||||
if( zIn[0]=='-' ){
|
||||
jsonAppendChar(pOut, '-');
|
||||
k++;
|
||||
|
@ -205423,11 +205445,12 @@ static u32 jsonTranslateBlobToText(
|
|||
jsonAppendChar(pOut, '[');
|
||||
j = i+n;
|
||||
iEnd = j+sz;
|
||||
while( j<iEnd ){
|
||||
while( j<iEnd && pOut->eErr==0 ){
|
||||
j = jsonTranslateBlobToText(pParse, j, pOut);
|
||||
jsonAppendChar(pOut, ',');
|
||||
}
|
||||
if( sz>0 ) pOut->nUsed--;
|
||||
if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
|
||||
if( sz>0 ) jsonStringTrimOneChar(pOut);
|
||||
jsonAppendChar(pOut, ']');
|
||||
break;
|
||||
}
|
||||
|
@ -205436,17 +205459,18 @@ static u32 jsonTranslateBlobToText(
|
|||
jsonAppendChar(pOut, '{');
|
||||
j = i+n;
|
||||
iEnd = j+sz;
|
||||
while( j<iEnd ){
|
||||
while( j<iEnd && pOut->eErr==0 ){
|
||||
j = jsonTranslateBlobToText(pParse, j, pOut);
|
||||
jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
|
||||
}
|
||||
if( x & 1 ) pOut->eErr |= JSTRING_MALFORMED;
|
||||
if( sz>0 ) pOut->nUsed--;
|
||||
if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
|
||||
if( sz>0 ) jsonStringTrimOneChar(pOut);
|
||||
jsonAppendChar(pOut, '}');
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
malformed_jsonb:
|
||||
pOut->eErr |= JSTRING_MALFORMED;
|
||||
break;
|
||||
}
|
||||
|
@ -206373,6 +206397,38 @@ jsonInsertIntoBlob_patherror:
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** If pArg is a blob that seems like a JSONB blob, then initialize
|
||||
** p to point to that JSONB and return TRUE. If pArg does not seem like
|
||||
** a JSONB blob, then return FALSE;
|
||||
**
|
||||
** This routine is only called if it is already known that pArg is a
|
||||
** blob. The only open question is whether or not the blob appears
|
||||
** to be a JSONB blob.
|
||||
*/
|
||||
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
|
||||
u32 n, sz = 0;
|
||||
p->aBlob = (u8*)sqlite3_value_blob(pArg);
|
||||
p->nBlob = (u32)sqlite3_value_bytes(pArg);
|
||||
if( p->nBlob==0 ){
|
||||
p->aBlob = 0;
|
||||
return 0;
|
||||
}
|
||||
if( NEVER(p->aBlob==0) ){
|
||||
return 0;
|
||||
}
|
||||
if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
|
||||
&& (n = jsonbPayloadSize(p, 0, &sz))>0
|
||||
&& sz+n==p->nBlob
|
||||
&& ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
p->aBlob = 0;
|
||||
p->nBlob = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
|
||||
** from the SQL function argument pArg. Return a pointer to the new
|
||||
|
@ -206429,29 +206485,24 @@ rebuild_from_cache:
|
|||
return p;
|
||||
}
|
||||
if( eType==SQLITE_BLOB ){
|
||||
u32 n, sz = 0;
|
||||
p->aBlob = (u8*)sqlite3_value_blob(pArg);
|
||||
p->nBlob = (u32)sqlite3_value_bytes(pArg);
|
||||
if( p->nBlob==0 ){
|
||||
goto json_pfa_malformed;
|
||||
if( jsonArgIsJsonb(pArg,p) ){
|
||||
if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
|
||||
goto json_pfa_oom;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
if( NEVER(p->aBlob==0) ){
|
||||
goto json_pfa_oom;
|
||||
}
|
||||
if( (p->aBlob[0] & 0x0f)>JSONB_OBJECT ){
|
||||
goto json_pfa_malformed;
|
||||
}
|
||||
n = jsonbPayloadSize(p, 0, &sz);
|
||||
if( n==0
|
||||
|| sz+n!=p->nBlob
|
||||
|| ((p->aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0)
|
||||
){
|
||||
goto json_pfa_malformed;
|
||||
}
|
||||
if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
|
||||
goto json_pfa_oom;
|
||||
}
|
||||
return p;
|
||||
/* If the blob is not valid JSONB, fall through into trying to cast
|
||||
** the blob into text which is then interpreted as JSON. (tag-20240123-a)
|
||||
**
|
||||
** This goes against all historical documentation about how the SQLite
|
||||
** JSON functions were suppose to work. From the beginning, blob was
|
||||
** reserved for expansion and a blob value should have raised an error.
|
||||
** But it did not, due to a bug. And many applications came to depend
|
||||
** upon this buggy behavior, espeically when using the CLI and reading
|
||||
** JSON text using readfile(), which returns a blob. For this reason
|
||||
** we will continue to support the bug moving forward.
|
||||
** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
|
||||
*/
|
||||
}
|
||||
p->zJson = (char*)sqlite3_value_text(pArg);
|
||||
p->nJson = sqlite3_value_bytes(pArg);
|
||||
|
@ -207427,12 +207478,12 @@ static void jsonValidFunc(
|
|||
return;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
if( (flags & 0x0c)!=0 && jsonFuncArgMightBeBinary(argv[0]) ){
|
||||
if( jsonFuncArgMightBeBinary(argv[0]) ){
|
||||
if( flags & 0x04 ){
|
||||
/* Superficial checking only - accomplished by the
|
||||
** jsonFuncArgMightBeBinary() call above. */
|
||||
res = 1;
|
||||
}else{
|
||||
}else if( flags & 0x08 ){
|
||||
/* Strict checking. Check by translating BLOB->TEXT->BLOB. If
|
||||
** no errors occur, call that a "strict check". */
|
||||
JsonParse px;
|
||||
|
@ -207443,8 +207494,11 @@ static void jsonValidFunc(
|
|||
iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
|
||||
res = iErr==0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* Fall through into interpreting the input as text. See note
|
||||
** above at tag-20240123-a. */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
default: {
|
||||
JsonParse px;
|
||||
|
@ -207569,7 +207623,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
|
|||
if( isFinal ){
|
||||
if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
|
||||
}else{
|
||||
pStr->nUsed--;
|
||||
jsonStringTrimOneChar(pStr);
|
||||
}
|
||||
return;
|
||||
}else if( isFinal ){
|
||||
|
@ -207579,7 +207633,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
|
|||
pStr->bStatic = 1;
|
||||
}else{
|
||||
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
||||
pStr->nUsed--;
|
||||
jsonStringTrimOneChar(pStr);
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
|
||||
|
@ -207689,7 +207743,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
|
|||
if( isFinal ){
|
||||
if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
|
||||
}else{
|
||||
pStr->nUsed--;
|
||||
jsonStringTrimOneChar(pStr);
|
||||
}
|
||||
return;
|
||||
}else if( isFinal ){
|
||||
|
@ -207699,7 +207753,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
|
|||
pStr->bStatic = 1;
|
||||
}else{
|
||||
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
||||
pStr->nUsed--;
|
||||
jsonStringTrimOneChar(pStr);
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
|
||||
|
@ -208180,13 +208234,9 @@ static int jsonEachFilter(
|
|||
memset(&p->sParse, 0, sizeof(p->sParse));
|
||||
p->sParse.nJPRef = 1;
|
||||
p->sParse.db = p->db;
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
||||
if( jsonFuncArgMightBeBinary(argv[0]) ){
|
||||
p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
|
||||
p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
|
||||
}else{
|
||||
goto json_each_malformed_input;
|
||||
}
|
||||
if( jsonFuncArgMightBeBinary(argv[0]) ){
|
||||
p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
|
||||
p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
|
||||
}else{
|
||||
p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
|
||||
p->sParse.nJson = sqlite3_value_bytes(argv[0]);
|
||||
|
@ -250495,7 +250545,7 @@ static void fts5SourceIdFunc(
|
|||
){
|
||||
assert( nArg==0 );
|
||||
UNUSED_PARAM2(nArg, apUnused);
|
||||
sqlite3_result_text(pCtx, "fts5: 2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d", -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -250526,27 +250576,21 @@ static int fts5IntegrityMethod(
|
|||
char **pzErr /* Write error message here */
|
||||
){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
Fts5Config *pConfig = pTab->p.pConfig;
|
||||
char *zSql;
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
|
||||
assert( pzErr!=0 && *pzErr==0 );
|
||||
UNUSED_PARAM(isQuick);
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
zSchema, zTabname, pConfig->zName);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_exec(pConfig->db, zSql, 0, 0, &zErr);
|
||||
sqlite3_free(zSql);
|
||||
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
|
||||
zSchema, zTabname);
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, zErr);
|
||||
zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}
|
||||
sqlite3_free(zErr);
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,9 +146,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.45.0"
|
||||
#define SQLITE_VERSION_NUMBER 3045000
|
||||
#define SQLITE_SOURCE_ID "2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d"
|
||||
#define SQLITE_VERSION "3.45.1"
|
||||
#define SQLITE_VERSION_NUMBER 3045001
|
||||
#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
|
|
@ -436,7 +436,7 @@ int _findCacheID(const int domainID, const int clientID, const enum query_type q
|
|||
dns_cache->clientID = clientID;
|
||||
dns_cache->query_type = query_type;
|
||||
dns_cache->force_reply = 0u;
|
||||
dns_cache->domainlist_id = -1; // -1 = not set
|
||||
dns_cache->list_id = -1; // -1 = not set
|
||||
|
||||
// Increase counter by one
|
||||
counters->dns_cache_size++;
|
||||
|
@ -568,7 +568,7 @@ void FTL_reset_per_client_domain_data(void)
|
|||
// Reset blocking status
|
||||
dns_cache->blocking_status = UNKNOWN_BLOCKED;
|
||||
// Reset domainlist ID
|
||||
dns_cache->domainlist_id = -1;
|
||||
dns_cache->list_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ typedef struct {
|
|||
enum query_type query_type;
|
||||
int domainID;
|
||||
int clientID;
|
||||
int domainlist_id;
|
||||
int list_id;
|
||||
char *cname_target;
|
||||
} DNSCacheData;
|
||||
|
||||
|
|
|
@ -1143,16 +1143,25 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
|||
}
|
||||
|
||||
// Check domain against antigravity
|
||||
int domain_id = -1;
|
||||
const enum db_result antigravity = in_gravity(domain, client, true, &domain_id);
|
||||
int list_id = -1;
|
||||
const enum db_result antigravity = in_gravity(domain, client, true, &list_id);
|
||||
if(antigravity == FOUND)
|
||||
{
|
||||
log_debug(DEBUG_QUERIES, "Allowing query due to antigravity match (ID %i)", domain_id);
|
||||
log_debug(DEBUG_QUERIES, "Allowing query due to antigravity match (list ID %i)", list_id);
|
||||
|
||||
// Store ID of the matching antigravity list
|
||||
// positive values (incl. 0) are used for domainlists
|
||||
// -1 means "not set"
|
||||
// -2 is gravity list 0
|
||||
// -3 is gravity list 1
|
||||
// ...
|
||||
dns_cache->list_id = -1 * (list_id + 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check domains against gravity domains
|
||||
const enum db_result gravity = in_gravity(domain, client, false, &domain_id);
|
||||
const enum db_result gravity = in_gravity(domain, client, false, &list_id);
|
||||
if(gravity == FOUND)
|
||||
{
|
||||
// Set new status
|
||||
|
@ -1162,6 +1171,12 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
|||
// Mark domain as gravity blocked for this client
|
||||
set_dnscache_blockingstatus(dns_cache, client, GRAVITY_BLOCKED, domain);
|
||||
|
||||
log_debug(DEBUG_QUERIES, "Blocking query due to gravity match (list ID %i)", list_id);
|
||||
|
||||
// Store ID of the matching gravity list
|
||||
// see remarks above for the list_id values
|
||||
dns_cache->list_id = -1 * (list_id + 2);
|
||||
|
||||
// We block this domain
|
||||
return true;
|
||||
}
|
||||
|
@ -1222,7 +1237,7 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
|||
cname_target = dns_cache->cname_target;
|
||||
|
||||
// Store ID of this regex (fork-private)
|
||||
last_regex_idx = dns_cache->domainlist_id;
|
||||
last_regex_idx = dns_cache->list_id;
|
||||
|
||||
// We block this domain
|
||||
return true;
|
||||
|
@ -1362,14 +1377,14 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
|||
// the lengthy tests below
|
||||
blockingreason = "regex denied";
|
||||
log_debug(DEBUG_QUERIES, "%s is known as %s (cache regex ID: %i)",
|
||||
domainstr, blockingreason, dns_cache->domainlist_id);
|
||||
domainstr, blockingreason, dns_cache->list_id);
|
||||
|
||||
// Do not block if the entire query is to be permitted as something
|
||||
// along the CNAME path hit the whitelist
|
||||
if(!query->flags.allowed)
|
||||
{
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
last_regex_idx = dns_cache->domainlist_id;
|
||||
last_regex_idx = dns_cache->list_id;
|
||||
query_blocked(query, domain, client, QUERY_REGEX);
|
||||
return true;
|
||||
}
|
||||
|
@ -1481,7 +1496,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
|||
if(config.debug.queries.v.b)
|
||||
{
|
||||
log_debug(DEBUG_QUERIES, "Blocking %s as %s is %s (domainlist ID: %i)",
|
||||
domainstr, blockedDomain, blockingreason, dns_cache->domainlist_id);
|
||||
domainstr, blockedDomain, blockingreason, dns_cache->list_id);
|
||||
if(force_next_DNS_reply != 0)
|
||||
log_debug(DEBUG_QUERIES, "Forcing next reply to %s", get_query_reply_str(force_next_DNS_reply));
|
||||
}
|
||||
|
@ -1496,7 +1511,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
|||
// Debug output
|
||||
// client is guaranteed to be non-NULL above
|
||||
log_debug(DEBUG_QUERIES, "DNS cache: %s/%s is %s (domainlist ID: %i)", getstr(client->ippos),
|
||||
domainstr, query->flags.allowed ? "whitelisted" : "not blocked", dns_cache->domainlist_id);
|
||||
domainstr, query->flags.allowed ? "whitelisted" : "not blocked", dns_cache->list_id);
|
||||
}
|
||||
|
||||
free(domainstr);
|
||||
|
@ -1606,8 +1621,8 @@ bool _FTL_CNAME(const char *dst, const char *src, const int id, const char* file
|
|||
|
||||
// Propagate ID of responsible regex up from the child to the parent
|
||||
// domain (but only if set)
|
||||
if(parent_cache != NULL && child_cache != NULL && child_cache->domainlist_id != -1)
|
||||
parent_cache->domainlist_id = child_cache->domainlist_id;
|
||||
if(parent_cache != NULL && child_cache != NULL && child_cache->list_id != -1)
|
||||
parent_cache->list_id = child_cache->list_id;
|
||||
|
||||
// Set status
|
||||
query_set_status(query, QUERY_REGEX_CNAME);
|
||||
|
@ -2967,10 +2982,16 @@ static char *get_ptrname(struct in_addr *addr)
|
|||
suffix = get_domain(*addr);
|
||||
else
|
||||
suffix = daemon->domain_suffix;
|
||||
|
||||
// If local suffix is not available, we try to obtain the domain from
|
||||
// the kernel similar to how we do it for the hostname
|
||||
if(!suffix)
|
||||
suffix = (char*)domainname();
|
||||
|
||||
// If local suffix is not available, we substitute "no_fqdn_available"
|
||||
// see the comment about PIHOLE_PTR=HOSTNAMEFQDN in the Pi-hole docs
|
||||
// for further details on why this was chosen
|
||||
if(!suffix)
|
||||
if(!suffix || suffix[0] == '\0')
|
||||
suffix = (char*)"no_fqdn_available";
|
||||
|
||||
// Get enough space for domain building
|
||||
|
|
|
@ -283,7 +283,7 @@ enum ptr_type {
|
|||
|
||||
enum addinfo_type {
|
||||
ADDINFO_CNAME_DOMAIN = 1,
|
||||
ADDINFO_REGEX_ID
|
||||
ADDINFO_LIST_ID
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum listening_mode {
|
||||
|
|
|
@ -548,7 +548,7 @@ bool in_regex(const char *domain, DNSCacheData *dns_cache, const int clientID, c
|
|||
if(regex_id != -1)
|
||||
{
|
||||
// We found a match
|
||||
dns_cache->domainlist_id = regex_id;
|
||||
dns_cache->list_id = regex_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
566
src/resolve.c
566
src/resolve.c
|
@ -31,8 +31,69 @@
|
|||
#include "events.h"
|
||||
// resolve_regex_cnames()
|
||||
#include "regex_r.h"
|
||||
// statis_assert()
|
||||
#include <assert.h>
|
||||
|
||||
static bool res_initialized = false;
|
||||
// Function Prototypes
|
||||
static void name_toDNS(unsigned char *dns, const size_t dnslen, const char *host, const size_t hostlen) __attribute__((nonnull(1,3)));
|
||||
static unsigned char *name_fromDNS(unsigned char *reader, unsigned char *buffer, uint16_t *count) __attribute__((malloc)) __attribute__((nonnull(1,2,3)));
|
||||
|
||||
// Avoid "error: packed attribute causes inefficient alignment for ..." on ARM32
|
||||
// builds due to the use of __attribute__((packed)) in the following structs
|
||||
// Their correct size is ensured for each by static_assert() below
|
||||
_Pragma("GCC diagnostic push")
|
||||
_Pragma("GCC diagnostic ignored \"-Wattributes\"")
|
||||
|
||||
// DNS header structure
|
||||
struct DNS_HEADER
|
||||
{
|
||||
uint16_t id; // identification number
|
||||
|
||||
bool rd :1; // recursion desired
|
||||
bool tc :1; // truncated message
|
||||
bool aa :1; // authoritative answer
|
||||
uint8_t opcode :4; // purpose of message
|
||||
bool qr :1; // query/response flag
|
||||
|
||||
uint8_t rcode :4; // response code
|
||||
bool cd :1; // checking disabled
|
||||
bool ad :1; // authenticated data
|
||||
bool z :1; // its z! reserved
|
||||
bool ra :1; // recursion available
|
||||
|
||||
uint16_t q_count; // number of question entries
|
||||
uint16_t ans_count; // number of answer entries
|
||||
uint16_t auth_count; // number of authority entries
|
||||
uint16_t add_count; // number of resource entries
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(struct DNS_HEADER) == 12);
|
||||
|
||||
// Constant sized fields of query structure
|
||||
struct QUESTION
|
||||
{
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
};
|
||||
static_assert(sizeof(struct QUESTION) == 4);
|
||||
|
||||
// Constant sized fields of the resource record structure
|
||||
struct R_DATA
|
||||
{
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
uint32_t ttl; // RFC 1035 defines the TTL field as "positive values of a signed 32bit number"
|
||||
uint16_t data_len;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(struct R_DATA) == 10);
|
||||
_Pragma("GCC diagnostic pop")
|
||||
|
||||
// Pointers to resource record contents
|
||||
struct RES_RECORD
|
||||
{
|
||||
unsigned char *name;
|
||||
struct R_DATA *resource;
|
||||
uint8_t *rdata;
|
||||
};
|
||||
|
||||
// Validate given hostname
|
||||
static bool valid_hostname(char* name, const char* clientip)
|
||||
|
@ -75,56 +136,6 @@ static bool valid_hostname(char* name, const char* clientip)
|
|||
return true;
|
||||
}
|
||||
|
||||
#define NUM_NS4 (sizeof(_res.nsaddr_list) / sizeof(_res.nsaddr_list[0]))
|
||||
#define NUM_NS6 (sizeof(_res._u._ext.nsaddrs) / sizeof(_res._u._ext.nsaddrs[0]))
|
||||
|
||||
static void print_used_resolvers(const char *message)
|
||||
{
|
||||
// Print details only when debugging
|
||||
if(!(config.debug.resolver.v.b))
|
||||
return;
|
||||
|
||||
log_debug(DEBUG_RESOLVER, "%s", message);
|
||||
for(unsigned int i = 0u; i < NUM_NS4 + NUM_NS6; i++)
|
||||
{
|
||||
int family;
|
||||
in_port_t port;
|
||||
void *addr = NULL;
|
||||
if(i < NUM_NS4)
|
||||
{
|
||||
// Regular name servers (IPv4)
|
||||
const unsigned int j = i;
|
||||
// Some of the entries may not be configured
|
||||
if(_res.nsaddr_list[j].sin_family != AF_INET)
|
||||
continue;
|
||||
|
||||
// IPv4 name servers
|
||||
addr = &_res.nsaddr_list[j].sin_addr;
|
||||
port = ntohs(_res.nsaddr_list[j].sin_port);
|
||||
family = _res.nsaddr_list[j].sin_family;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extension name servers (IPv6)
|
||||
const unsigned int j = i - NUM_NS4;
|
||||
// Some of the entries may not be configured
|
||||
if(_res._u._ext.nsaddrs[j] == NULL ||
|
||||
_res._u._ext.nsaddrs[j]->sin6_family != AF_INET6)
|
||||
continue;
|
||||
addr = &_res._u._ext.nsaddrs[j]->sin6_addr;
|
||||
port = ntohs(_res._u._ext.nsaddrs[j]->sin6_port);
|
||||
family = _res._u._ext.nsaddrs[j]->sin6_family;
|
||||
}
|
||||
|
||||
// Convert nameserver information to human-readable form
|
||||
char nsname[INET6_ADDRSTRLEN];
|
||||
inet_ntop(family, addr, nsname, INET6_ADDRSTRLEN);
|
||||
|
||||
log_debug(DEBUG_RESOLVER, " %s %u: %s:%hu (IPv%u)", i < MAXNS ? " " : "EXT",
|
||||
i, nsname, port, family == AF_INET ? 4u : 6u);
|
||||
}
|
||||
}
|
||||
|
||||
// Return if we want to resolve address to names at all
|
||||
// (may be disabled due to config settings)
|
||||
bool __attribute__((pure)) resolve_names(void)
|
||||
|
@ -143,13 +154,257 @@ bool __attribute__((pure)) resolve_this_name(const char *ipaddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
char *resolveHostname(const char *addr)
|
||||
// Perform a name lookup by sending a packet to ourselves
|
||||
static char *__attribute__((malloc)) ngethostbyname(const char *host, const char *ipaddr)
|
||||
{
|
||||
uint8_t buf[1024] = { 0 };
|
||||
uint8_t *qname = NULL, *reader = NULL;
|
||||
struct RES_RECORD answers[20] = { 0 }; // buffer for DNS replies
|
||||
struct DNS_HEADER *dns = NULL;
|
||||
struct QUESTION *qinfo = NULL;
|
||||
|
||||
// Set the DNS structure to standard queries
|
||||
dns = (struct DNS_HEADER *)&buf;
|
||||
dns->id = (unsigned short) htons(random()); // random query ID
|
||||
dns->qr = 0; // This is a query
|
||||
dns->opcode = 0; // This is a standard query
|
||||
dns->aa = 0; // Not Authoritative
|
||||
dns->tc = 0; // This message is not truncated
|
||||
dns->rd = 1; // Recursion Desired
|
||||
dns->ra = 0; // Recursion not available!
|
||||
dns->z = 0; // Reserved
|
||||
dns->ad = 0; // This is not an authenticated answer
|
||||
dns->cd = 0; // Checking Disabled
|
||||
dns->rcode = 0; // Response code
|
||||
dns->q_count = htons(1); // 1 question
|
||||
dns->ans_count = 0; // No answers
|
||||
dns->auth_count = 0; // No authority
|
||||
dns->add_count = 0; // No additional
|
||||
|
||||
// Point to the query portion
|
||||
qname = &buf[sizeof(struct DNS_HEADER)];
|
||||
|
||||
// Make a copy of the hostname with two extra bytes for the length and
|
||||
// the final dot, copy the hostname into it and convert to convert to
|
||||
// DNS format
|
||||
const size_t hnamelen = strlen(host) + 2;
|
||||
char *hname = calloc(hnamelen, sizeof(char));
|
||||
if(hname == NULL)
|
||||
{
|
||||
log_err("Unable to allocate memory for hname");
|
||||
return strdup("");
|
||||
}
|
||||
strncpy(hname, host, hnamelen);
|
||||
strncat(hname, ".", hnamelen - strlen(hname));
|
||||
hname[hnamelen - 1] = '\0';
|
||||
|
||||
name_toDNS(qname, sizeof(buf) - sizeof(struct DNS_HEADER), hname, hnamelen);
|
||||
free(hname);
|
||||
qinfo = (void*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)];
|
||||
|
||||
qinfo->qtype = htons(T_PTR); // Type of the query, A, MX, CNAME, NS etc
|
||||
qinfo->qclass = htons(1); // IN
|
||||
|
||||
// UDP packet for DNS queries
|
||||
const int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
struct sockaddr_in dest = { 0 };
|
||||
dest.sin_family = AF_INET; // IPv4
|
||||
dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 127.0.0.1
|
||||
dest.sin_port = htons(config.dns.port.v.u16); // Configured DNS port
|
||||
|
||||
const size_t questionlen = sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1) + sizeof(struct QUESTION);
|
||||
if(sendto(s, buf, questionlen, 0, (struct sockaddr*)&dest, sizeof(dest)) < 0)
|
||||
{
|
||||
perror("sendto failed");
|
||||
close(s);
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
// Receive the answer
|
||||
socklen_t addrlen = sizeof(dest);
|
||||
if(recvfrom (s, buf, sizeof(buf), 0, (struct sockaddr*)&dest, &addrlen) < 0)
|
||||
{
|
||||
perror("recvfrom failed");
|
||||
close(s);
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
// Close socket
|
||||
close(s);
|
||||
|
||||
// Parse the reply
|
||||
dns = (struct DNS_HEADER*) buf;
|
||||
// Move ahead of the dns header and the query field
|
||||
reader = &buf[questionlen];
|
||||
|
||||
// Start reading answers
|
||||
uint16_t stop = 0;
|
||||
char *name = NULL;
|
||||
for(uint16_t i = 0; i < ntohs(dns->ans_count); i++)
|
||||
{
|
||||
answers[i].name = name_fromDNS(reader, buf, &stop);
|
||||
reader = reader + stop;
|
||||
|
||||
answers[i].resource = (struct R_DATA*)(reader);
|
||||
reader = reader + sizeof(struct R_DATA);
|
||||
|
||||
// We only care about PTR answers and ignore all others
|
||||
if(ntohs(answers[i].resource->type) != T_PTR)
|
||||
continue;
|
||||
|
||||
// Read the answer and convert from network to host representation
|
||||
answers[i].rdata = name_fromDNS(reader, buf, &stop);
|
||||
reader = reader + stop;
|
||||
|
||||
name = (char *)answers[i].rdata;
|
||||
log_debug(DEBUG_RESOLVER, "Resolving %s (PTR \"%s\"): %u = \"%s\"",
|
||||
ipaddr, answers[i].name, i, answers[i].rdata);
|
||||
|
||||
// We break out of the loop if this is a valid hostname
|
||||
if(valid_hostname(name, ipaddr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discard this answer: free memory and set name to NULL
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(name != NULL)
|
||||
{
|
||||
// We have a valid hostname, return it
|
||||
// This is allocated memory
|
||||
return name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No valid hostname found, return empty string
|
||||
return strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
// Convert hostname from network to host representation
|
||||
// This routine supports DNS compression pointers
|
||||
// 3www6google3com -> www.google.com
|
||||
static u_char * __attribute__((malloc)) __attribute__((nonnull(1,2,3))) name_fromDNS(unsigned char *reader, unsigned char *buffer, uint16_t *count)
|
||||
{
|
||||
unsigned char *name = calloc(MAXHOSTNAMELEN, sizeof(char));
|
||||
unsigned int p = 0, jumped = 0;
|
||||
|
||||
// Initialize count
|
||||
*count = 1;
|
||||
|
||||
// Parse DNS label string encoding (e.g, 3www6google3com)
|
||||
//
|
||||
// In its text format, a domain name is a sequence of dot-separated
|
||||
// "labels". The dot separators are not used in binary DNS messages.
|
||||
// Instead, each label is preceded by a byte containing its length, and
|
||||
// the name is terminated by a zero-length label representing the root
|
||||
// zone.
|
||||
while(*reader != 0)
|
||||
{
|
||||
if(*reader >= 0xC0)
|
||||
{
|
||||
// RFC 1035, Section 4.1.4: Message compression
|
||||
//
|
||||
// A label can be up to 63 bytes long; if the length
|
||||
// byte is 64 (0x40) or greater, it has a special
|
||||
// meaning. Values between 0x40 and 0xBF have no purpose
|
||||
// except to cause painful memories for those involved
|
||||
// in DNS extensions in the late 1990s.
|
||||
//
|
||||
// However, if the length byte is 0xC0 or greater, the
|
||||
// length byte and the next byte form a "compression
|
||||
// pointer". A DNS name compression pointer allows DNS
|
||||
// messages to reuse parent domains. The lower 14 bits
|
||||
// are an offset into the DNS message where the
|
||||
// remaining suffix of the name previously occurred.
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | 1 1| OFFSET |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// The first two bits are ones. This allows a pointer
|
||||
// to be distinguished from a label, since the label
|
||||
// must begin with two zero bits because labels are
|
||||
// restricted to 63 octets or less. See the referenced
|
||||
// RFC for more details.
|
||||
const unsigned int offset = ((*reader - 0xC0) << 8) + *(reader + 1);
|
||||
reader = buffer + offset - 1;
|
||||
jumped = 1; // We have jumped to another location so counting won't go up
|
||||
}
|
||||
else
|
||||
// Copy character to name
|
||||
name[p++] = *reader;
|
||||
|
||||
// Increment read pointer
|
||||
reader = reader + 1;
|
||||
|
||||
if(jumped == 0)
|
||||
*count = *count + 1; // If we haven't jumped to another location then we can count up
|
||||
}
|
||||
|
||||
// Terminate string
|
||||
name[p] = '\0';
|
||||
|
||||
// Number of steps we actually moved forward in the packet
|
||||
if(jumped == 1)
|
||||
*count += 1;
|
||||
|
||||
// Now convert 3www6google3com0 to www.google.com
|
||||
unsigned int i = 0;
|
||||
for(; i < strlen((const char*)name); i++)
|
||||
{
|
||||
p = name[i];
|
||||
for(unsigned j = 0; j < p; j++)
|
||||
{
|
||||
name[i] = name[i + 1];
|
||||
i = i + 1;
|
||||
}
|
||||
name[i] = '.';
|
||||
}
|
||||
|
||||
// Strip off the trailing dot
|
||||
name[i-1] = '\0';
|
||||
return name;
|
||||
}
|
||||
|
||||
// Convert hostname from host to network representation
|
||||
// www.google.com -> 3www6google3com
|
||||
// We do not use DNS compression pointers here as we do not know if the DNS
|
||||
// server we are talking to supports them
|
||||
static void __attribute__((nonnull(1,3))) name_toDNS(unsigned char *dns, const size_t dnslen, const char *host, const size_t hostlen)
|
||||
{
|
||||
unsigned int lock = 0;
|
||||
|
||||
// Iterate over hostname characters
|
||||
for(unsigned int i = 0 ; i < strlen((char*)host); i++)
|
||||
{
|
||||
// If we encounter a dot, write the number of characters since the last dot
|
||||
// and then write the characters themselves
|
||||
if(host[i] == '.')
|
||||
{
|
||||
*dns++ = i - lock;
|
||||
for(;lock < i; lock++)
|
||||
*dns++ = host[lock];
|
||||
lock++;
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate the string at the end
|
||||
*dns++='\0';
|
||||
}
|
||||
|
||||
char *__attribute__((malloc)) resolveHostname(const char *addr, const bool force)
|
||||
{
|
||||
// Get host name
|
||||
char *hostn = NULL;
|
||||
|
||||
// Check if we want to resolve host names
|
||||
if(!resolve_this_name(addr))
|
||||
if(!force && !resolve_this_name(addr))
|
||||
{
|
||||
log_debug(DEBUG_RESOLVER, "Configured to not resolve host name for %s", addr);
|
||||
|
||||
|
@ -177,15 +432,6 @@ char *resolveHostname(const char *addr)
|
|||
return hostn;
|
||||
}
|
||||
|
||||
// Check if we want to resolve host names
|
||||
if(!resolve_this_name(addr))
|
||||
{
|
||||
log_debug(DEBUG_RESOLVER, "Configured to not resolve host name for %s", addr);
|
||||
|
||||
// Return an empty host name
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
// Test if we want to resolve an IPv6 address
|
||||
bool IPv6 = false;
|
||||
if(strstr(addr,":") != NULL)
|
||||
|
@ -193,6 +439,7 @@ char *resolveHostname(const char *addr)
|
|||
|
||||
// Convert address into binary form
|
||||
struct sockaddr_storage ss = { 0 };
|
||||
char *inaddr = NULL;
|
||||
if(IPv6)
|
||||
{
|
||||
// Get binary form of IPv6 address
|
||||
|
@ -202,6 +449,47 @@ char *resolveHostname(const char *addr)
|
|||
log_warn("Invalid IPv6 address when trying to resolve hostname: %s", addr);
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
// Need extra space for ".ip6.arpa" suffix
|
||||
// The 1.2.3.4... string is 63 + terminating \0 = 64 bytes long
|
||||
inaddr = calloc(64 + 10, sizeof(char));
|
||||
if(inaddr == NULL)
|
||||
{
|
||||
log_err("Unable to allocate memory for reverse lookup");
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
// Convert IPv6 address to reverse lookup format
|
||||
// b a 9 8 7 6 5 4 |<-- :: -->| 0 1 2 3 4
|
||||
// 4321:0::4:567:89ab -> b.a.9.8.7.6.5.0.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.2.3.4
|
||||
for(int i = 0; i < 32; i++)
|
||||
{
|
||||
// Get current nibble
|
||||
uint8_t nibble = ((uint8_t *)&(((struct sockaddr_in6 *)&ss)->sin6_addr))[i/2];
|
||||
|
||||
// Get lower nibble for even i, upper nibble for odd i
|
||||
if(i % 2 == 0)
|
||||
nibble = nibble >> 4;
|
||||
|
||||
// Mask out upper nibble
|
||||
nibble = nibble & 0x0F;
|
||||
|
||||
// Convert to ASCII
|
||||
char c = '0' + nibble;
|
||||
if(c > '9')
|
||||
c = 'a' + nibble - 10;
|
||||
|
||||
// Prepend to string
|
||||
inaddr[62-2*i] = c;
|
||||
|
||||
// Add dot after (actually: before) every nibble except
|
||||
// the last one
|
||||
if(i != 31)
|
||||
inaddr[62-2*i-1] = '.';
|
||||
}
|
||||
|
||||
// Add suffix
|
||||
strcat(inaddr, ".ip6.arpa");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -212,148 +500,28 @@ char *resolveHostname(const char *addr)
|
|||
log_warn("Invalid IPv4 address when trying to resolve hostname: %s", addr);
|
||||
return strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize resolver subroutines if trying to resolve for the first time
|
||||
// res_init() reads resolv.conf to get the default domain name and name server
|
||||
// address(es). If no server is given, the local host is tried. If no domain
|
||||
// is given, that associated with the local host is used.
|
||||
if(!res_initialized)
|
||||
{
|
||||
res_init();
|
||||
res_initialized = true;
|
||||
}
|
||||
|
||||
// INADDR_LOOPBACK is in host byte order, however, in_addr has to be in
|
||||
// network byte order, convert it here if necessary
|
||||
struct in_addr FTLaddr = { htonl(INADDR_LOOPBACK) };
|
||||
in_port_t FTLport = htons(config.dns.port.v.u16);
|
||||
|
||||
// Temporarily set FTL as system resolver
|
||||
|
||||
// Backup configured name servers and invalidate them (IPv4)
|
||||
struct in_addr ns_addr_bck[MAXNS];
|
||||
in_port_t ns_port_bck[MAXNS];
|
||||
int bck_nscount = _res.nscount;
|
||||
for(unsigned int i = 0u; i < NUM_NS4; i++)
|
||||
{
|
||||
ns_addr_bck[i] = _res.nsaddr_list[i].sin_addr;
|
||||
ns_port_bck[i] = _res.nsaddr_list[i].sin_port;
|
||||
_res.nsaddr_list[i].sin_addr.s_addr = 0; // 0.0.0.0
|
||||
}
|
||||
|
||||
// Set FTL at 127.0.0.1 as the only resolver
|
||||
_res.nsaddr_list[0].sin_addr.s_addr = FTLaddr.s_addr;
|
||||
// Set resolver port
|
||||
_res.nsaddr_list[0].sin_port = FTLport;
|
||||
// Configure resolver to use only one resolver
|
||||
_res.nscount = 1;
|
||||
|
||||
// Backup configured name server and invalidate them (IPv6)
|
||||
struct in6_addr ns6_addr_bck[MAXNS];
|
||||
in_port_t ns6_port_bck[MAXNS];
|
||||
int bck_nscount6 = _res._u._ext.nscount6;
|
||||
for(unsigned int i = 0u; i < NUM_NS6; i++)
|
||||
{
|
||||
if(_res._u._ext.nsaddrs[i] == NULL)
|
||||
continue;
|
||||
memcpy(&ns6_addr_bck[i], &_res._u._ext.nsaddrs[i]->sin6_addr, sizeof(struct in6_addr));
|
||||
ns6_port_bck[i] = _res._u._ext.nsaddrs[i]->sin6_port;
|
||||
memcpy(&_res._u._ext.nsaddrs[i]->sin6_addr, &in6addr_any, sizeof(struct in6_addr));
|
||||
}
|
||||
|
||||
// Set FTL as the only resolver only when IPv6 is enabled
|
||||
if(bck_nscount6 > 0)
|
||||
{
|
||||
// Set FTL at ::1 as the only resolver
|
||||
memcpy(&_res._u._ext.nsaddrs[0]->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr));
|
||||
// Set resolver port
|
||||
_res._u._ext.nsaddrs[0]->sin6_port = FTLport;
|
||||
// Configure resolver to use only one resolver
|
||||
_res._u._ext.nscount6 = 1;
|
||||
}
|
||||
|
||||
if(config.debug.resolver.v.b)
|
||||
print_used_resolvers("Set nameservers to:");
|
||||
|
||||
// Try to resolve address
|
||||
char host[NI_MAXHOST] = { 0 };
|
||||
int ret = getnameinfo((struct sockaddr*)&ss, sizeof(ss), host, sizeof(host), NULL, 0, NI_NAMEREQD);
|
||||
|
||||
// Check if getnameinfo() returned a host name
|
||||
if(ret == 0)
|
||||
{
|
||||
if(valid_hostname(host, addr))
|
||||
// Need extra space for ".in-addr.arpa" suffix
|
||||
inaddr = calloc(INET_ADDRSTRLEN + 14, sizeof(char));
|
||||
if(inaddr == NULL)
|
||||
{
|
||||
// Return hostname copied to new memory location
|
||||
hostn = strdup(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostn = strdup("[invalid host name]");
|
||||
log_err("Unable to allocate memory for reverse lookup");
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"%s\" (found internally)", hostn);
|
||||
}
|
||||
else
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"\" (not found internally: %s", gai_strerror(ret));
|
||||
|
||||
// Restore IPv4 resolvers
|
||||
for(unsigned int i = 0u; i < NUM_NS4; i++)
|
||||
{
|
||||
_res.nsaddr_list[i].sin_addr = ns_addr_bck[i];
|
||||
_res.nsaddr_list[i].sin_port = ns_port_bck[i];
|
||||
}
|
||||
_res.nscount = bck_nscount;
|
||||
|
||||
// Restore IPv6 resolvers
|
||||
for(unsigned int i = 0u; i < NUM_NS6; i++)
|
||||
{
|
||||
if(_res._u._ext.nsaddrs[i] == NULL)
|
||||
continue;
|
||||
memcpy(&_res._u._ext.nsaddrs[i]->sin6_addr, &ns6_addr_bck[i], sizeof(struct in6_addr));
|
||||
_res._u._ext.nsaddrs[i]->sin6_port = ns6_port_bck[i];
|
||||
}
|
||||
_res._u._ext.nscount6 = bck_nscount6;
|
||||
|
||||
if(config.debug.resolver.v.b)
|
||||
print_used_resolvers("Restored nameservers to:");
|
||||
|
||||
// If no host name was found before, try again with system-configured
|
||||
// resolvers (necessary for docker and friends)
|
||||
if(hostn == NULL)
|
||||
{
|
||||
// Try to resolve address
|
||||
ret = getnameinfo((struct sockaddr*)&ss, sizeof(ss), host, sizeof(host), NULL, 0, NI_NAMEREQD);
|
||||
|
||||
// Check if getnameinfo() returned a host name this time
|
||||
// First check for he not being NULL before trying to dereference it
|
||||
if(ret == 0)
|
||||
{
|
||||
if(valid_hostname(host, addr))
|
||||
{
|
||||
// Return hostname copied to new memory location
|
||||
hostn = strdup(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostn = strdup("[invalid host name]");
|
||||
}
|
||||
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"%s\" (found externally)", hostn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No hostname found (empty PTR)
|
||||
hostn = strdup("");
|
||||
|
||||
if(config.debug.resolver.v.b)
|
||||
log_debug(DEBUG_RESOLVER, " ---> \"\" (not found externally: %s)", gai_strerror(ret));
|
||||
}
|
||||
// Convert IPv4 address to reverse lookup format
|
||||
// 12.34.56.78 -> 78.56.34.12.in-addr.arpa
|
||||
snprintf(inaddr, INET_ADDRSTRLEN + 14, "%d.%d.%d.%d.in-addr.arpa",
|
||||
(int)((uint8_t *)&(((struct sockaddr_in *)&ss)->sin_addr))[3],
|
||||
(int)((uint8_t *)&(((struct sockaddr_in *)&ss)->sin_addr))[2],
|
||||
(int)((uint8_t *)&(((struct sockaddr_in *)&ss)->sin_addr))[1],
|
||||
(int)((uint8_t *)&(((struct sockaddr_in *)&ss)->sin_addr))[0]);
|
||||
}
|
||||
|
||||
// Return result
|
||||
return hostn;
|
||||
// Get host name by making a reverse lookup to ourselves (server at 127.0.0.1 with port 53)
|
||||
// We implement a minimalistic resolver here as we cannot rely on the system resolver using whatever
|
||||
// nameserver we configured in /etc/resolv.conf
|
||||
return ngethostbyname(inaddr, addr);
|
||||
}
|
||||
|
||||
// Resolve upstream destination host names
|
||||
|
@ -382,7 +550,7 @@ static size_t resolveAndAddHostname(size_t ippos, size_t oldnamepos)
|
|||
|
||||
// Important: Don't hold a lock while resolving as the main thread
|
||||
// (dnsmasq) needs to be operable during the call to resolveHostname()
|
||||
char *newname = resolveHostname(ipaddr);
|
||||
char *newname = resolveHostname(ipaddr, false);
|
||||
|
||||
// If no hostname was found, try to obtain hostname from the network table
|
||||
// This may be disabled due to a user setting
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define RESOLVE_H
|
||||
|
||||
void *DNSclient_thread(void *val);
|
||||
char *resolveHostname(const char *addr);
|
||||
char *resolveHostname(const char *addr, const bool force) __attribute__((malloc));
|
||||
bool resolve_names(void) __attribute__((pure));
|
||||
bool resolve_this_name(const char *ipaddr) __attribute__((pure));
|
||||
|
||||
|
|
|
@ -78,21 +78,81 @@ static int generate_private_key_ec(mbedtls_pk_context *key,
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Write a key and/or certificate to a file
|
||||
static bool write_to_file(const char *filename, const char *type, const char *suffix, const char *cert, const char *key)
|
||||
{
|
||||
// Create file with CA certificate only
|
||||
char *targetname = calloc(strlen(filename) + (suffix != NULL ? strlen(suffix) : 0) + 1, sizeof(char));
|
||||
strcpy(targetname, filename);
|
||||
|
||||
if(suffix != NULL)
|
||||
{
|
||||
// If the certificate file name ends with ".pem", replace it
|
||||
// with the specified suffix. Otherwise, append the specified
|
||||
// suffix to the certificate file name
|
||||
if (strlen(targetname) > 4 && strcmp(targetname + strlen(targetname) - 4, ".pem") == 0)
|
||||
targetname[strlen(filename) - 4] = '\0';
|
||||
|
||||
strcat(targetname, suffix);
|
||||
}
|
||||
|
||||
printf("Storing %s in %s ...\n", type, targetname);
|
||||
FILE *f = NULL;
|
||||
if ((f = fopen(targetname, "wb")) == NULL)
|
||||
{
|
||||
printf("ERROR: Could not open %s for writing\n", targetname);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write key (if provided)
|
||||
if(key != NULL)
|
||||
{
|
||||
const size_t olen = strlen((char *) key);
|
||||
if (fwrite(key, 1, olen, f) != olen)
|
||||
{
|
||||
printf("ERROR: Could not write key to %s\n", targetname);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write certificate (if provided)
|
||||
if(cert != NULL)
|
||||
{
|
||||
const size_t olen = strlen((char *) cert);
|
||||
if (fwrite(cert, 1, olen, f) != olen)
|
||||
{
|
||||
printf("ERROR: Could not write certificate to %s\n", targetname);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close cert file
|
||||
fclose(f);
|
||||
free(targetname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generate_certificate(const char* certfile, bool rsa, const char *domain)
|
||||
{
|
||||
int ret;
|
||||
mbedtls_x509write_cert crt;
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_x509write_cert ca_cert, server_cert;
|
||||
mbedtls_pk_context ca_key, server_key;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
const char *pers = "pihole-FTL";
|
||||
unsigned char ca_buffer[BUFFER_SIZE];
|
||||
unsigned char cert_buffer[BUFFER_SIZE];
|
||||
unsigned char key_buffer[BUFFER_SIZE];
|
||||
size_t olen = 0;
|
||||
unsigned char ca_key_buffer[BUFFER_SIZE];
|
||||
|
||||
// Initialize structures
|
||||
mbedtls_x509write_crt_init(&crt);
|
||||
mbedtls_pk_init(&key);
|
||||
mbedtls_x509write_crt_init(&ca_cert);
|
||||
mbedtls_x509write_crt_init(&server_cert);
|
||||
mbedtls_pk_init(&ca_key);
|
||||
mbedtls_pk_init(&server_key);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
|
||||
|
@ -110,7 +170,12 @@ bool generate_certificate(const char* certfile, bool rsa, const char *domain)
|
|||
{
|
||||
// Generate RSA key
|
||||
printf("Generating RSA key...\n");
|
||||
if((ret = generate_private_key_rsa(&key, &ctr_drbg, key_buffer)) != 0)
|
||||
if((ret = generate_private_key_rsa(&ca_key, &ctr_drbg, ca_key_buffer)) != 0)
|
||||
{
|
||||
printf("ERROR: generate_private_key returned %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
if((ret = generate_private_key_rsa(&server_key, &ctr_drbg, key_buffer)) != 0)
|
||||
{
|
||||
printf("ERROR: generate_private_key returned %d\n", ret);
|
||||
return false;
|
||||
|
@ -120,7 +185,12 @@ bool generate_certificate(const char* certfile, bool rsa, const char *domain)
|
|||
{
|
||||
// Generate EC key
|
||||
printf("Generating EC key...\n");
|
||||
if((ret = generate_private_key_ec(&key, &ctr_drbg, key_buffer)) != 0)
|
||||
if((ret = generate_private_key_ec(&ca_key, &ctr_drbg, ca_key_buffer)) != 0)
|
||||
{
|
||||
printf("ERROR: generate_private_key_ec returned %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
if((ret = generate_private_key_ec(&server_key, &ctr_drbg, key_buffer)) != 0)
|
||||
{
|
||||
printf("ERROR: generate_private_key_ec returned %d\n", ret);
|
||||
return false;
|
||||
|
@ -139,48 +209,73 @@ bool generate_certificate(const char* certfile, bool rsa, const char *domain)
|
|||
// only one certificate being issued with a given browser. Any new generated
|
||||
// certificate would be rejected by the browser as it would have the same
|
||||
// serial number as the previous one and uniques is violated.
|
||||
unsigned char serial[16] = { 0 };
|
||||
mbedtls_ctr_drbg_random(&ctr_drbg, serial, sizeof(serial));
|
||||
for(unsigned int i = 0; i < sizeof(serial) - 1; i++)
|
||||
serial[i] = '0' + (serial[i] % 10);
|
||||
serial[sizeof(serial) - 1] = '\0';
|
||||
unsigned char serial1[16] = { 0 }, serial2[16] = { 0 };
|
||||
mbedtls_ctr_drbg_random(&ctr_drbg, serial1, sizeof(serial1));
|
||||
for(unsigned int i = 0; i < sizeof(serial1) - 1; i++)
|
||||
serial1[i] = '0' + (serial1[i] % 10);
|
||||
serial1[sizeof(serial1) - 1] = '\0';
|
||||
mbedtls_ctr_drbg_random(&ctr_drbg, serial2, sizeof(serial2));
|
||||
for(unsigned int i = 0; i < sizeof(serial2) - 1; i++)
|
||||
serial2[i] = '0' + (serial2[i] % 10);
|
||||
serial2[sizeof(serial2) - 1] = '\0';
|
||||
|
||||
// Create validity period
|
||||
// Use YYYYMMDDHHMMSS as required by RFC 5280
|
||||
// Use YYYYMMDDHHMMSS as required by RFC 5280 (UTCTime)
|
||||
const time_t now = time(NULL);
|
||||
struct tm tms = { 0 };
|
||||
struct tm *tm = localtime_r(&now, &tms);
|
||||
struct tm *tm = gmtime_r(&now, &tms);
|
||||
char not_before[16] = { 0 };
|
||||
char not_after[16] = { 0 };
|
||||
strftime(not_before, sizeof(not_before), "%Y%m%d%H%M%S", tm);
|
||||
tm->tm_year += 30; // 30 years from now
|
||||
strftime(not_after, sizeof(not_after), "%Y%m%d%H%M%S", tm);
|
||||
|
||||
// Generate certificate
|
||||
printf("Generating new certificate with serial number %s...\n", serial);
|
||||
mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3);
|
||||
// 1. Create CA certificate
|
||||
printf("Generating new CA with serial number %s...\n", serial1);
|
||||
mbedtls_x509write_crt_set_version(&ca_cert, MBEDTLS_X509_CRT_VERSION_3);
|
||||
|
||||
mbedtls_x509write_crt_set_serial_raw(&crt, serial, sizeof(serial)-1);
|
||||
mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256);
|
||||
mbedtls_x509write_crt_set_subject_key(&crt, &key);
|
||||
mbedtls_x509write_crt_set_issuer_key(&crt, &key);
|
||||
mbedtls_x509write_crt_set_issuer_name(&crt, "CN=pi.hole");
|
||||
mbedtls_x509write_crt_set_validity(&crt, not_before, not_after);
|
||||
mbedtls_x509write_crt_set_basic_constraints(&crt, 0, -1);
|
||||
mbedtls_x509write_crt_set_subject_key_identifier(&crt);
|
||||
mbedtls_x509write_crt_set_authority_key_identifier(&crt);
|
||||
mbedtls_x509write_crt_set_serial_raw(&ca_cert, serial1, sizeof(serial1)-1);
|
||||
mbedtls_x509write_crt_set_md_alg(&ca_cert, MBEDTLS_MD_SHA256);
|
||||
mbedtls_x509write_crt_set_subject_key(&ca_cert, &ca_key);
|
||||
mbedtls_x509write_crt_set_subject_key_identifier(&ca_cert);
|
||||
mbedtls_x509write_crt_set_issuer_key(&ca_cert, &ca_key);
|
||||
mbedtls_x509write_crt_set_authority_key_identifier(&ca_cert);
|
||||
mbedtls_x509write_crt_set_issuer_name(&ca_cert, "CN=pi.hole,O=Pi-hole,C=DE");
|
||||
mbedtls_x509write_crt_set_subject_name(&ca_cert, "CN=pi.hole,O=Pi-hole,C=DE");
|
||||
mbedtls_x509write_crt_set_validity(&ca_cert, not_before, not_after);
|
||||
mbedtls_x509write_crt_set_basic_constraints(&ca_cert, 1, -1);
|
||||
|
||||
// Export CA in PEM format
|
||||
if((ret = mbedtls_x509write_crt_pem(&ca_cert, ca_buffer, sizeof(ca_buffer),
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
|
||||
{
|
||||
printf("ERROR: mbedtls_x509write_crt_pem (CA) returned %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Generating new server certificate with serial number %s...\n", serial2);
|
||||
mbedtls_x509write_crt_set_version(&server_cert, MBEDTLS_X509_CRT_VERSION_3);
|
||||
|
||||
mbedtls_x509write_crt_set_serial_raw(&server_cert, serial2, sizeof(serial2)-1);
|
||||
mbedtls_x509write_crt_set_md_alg(&server_cert, MBEDTLS_MD_SHA256);
|
||||
mbedtls_x509write_crt_set_subject_key(&server_cert, &server_key);
|
||||
mbedtls_x509write_crt_set_subject_key_identifier(&server_cert);
|
||||
mbedtls_x509write_crt_set_issuer_key(&server_cert, &ca_key);
|
||||
mbedtls_x509write_crt_set_authority_key_identifier(&server_cert);
|
||||
// subject name set below
|
||||
mbedtls_x509write_crt_set_issuer_name(&server_cert, "CN=pi.hole,O=Pi-hole,C=DE");
|
||||
mbedtls_x509write_crt_set_validity(&server_cert, not_before, not_after);
|
||||
mbedtls_x509write_crt_set_basic_constraints(&server_cert, 0, -1);
|
||||
|
||||
// Set subject name depending on the (optionally) specified domain
|
||||
{
|
||||
char *subject_name = calloc(strlen(domain) + 4, sizeof(char));
|
||||
strcpy(subject_name, "CN=");
|
||||
strcat(subject_name, domain);
|
||||
mbedtls_x509write_crt_set_subject_name(&crt, subject_name);
|
||||
mbedtls_x509write_crt_set_subject_name(&server_cert, subject_name);
|
||||
free(subject_name);
|
||||
}
|
||||
|
||||
|
||||
// Add "DNS:pi.hole" as subject alternative name (SAN)
|
||||
//
|
||||
// Since RFC 2818 (May 2000), the Common Name (CN) field is ignored
|
||||
|
@ -209,85 +304,32 @@ bool generate_certificate(const char* certfile, bool rsa, const char *domain)
|
|||
san_dns_pihole.next = &san_dns_domain; // Link this domain
|
||||
}
|
||||
|
||||
ret = mbedtls_x509write_crt_set_subject_alternative_name(&crt, &san_dns_pihole);
|
||||
ret = mbedtls_x509write_crt_set_subject_alternative_name(&server_cert, &san_dns_pihole);
|
||||
if (ret != 0)
|
||||
printf("mbedtls_x509write_crt_set_subject_alternative_name returned %d\n", ret);
|
||||
|
||||
|
||||
// Export certificate in PEM format
|
||||
if((ret = mbedtls_x509write_crt_pem(&crt, cert_buffer, sizeof(cert_buffer),
|
||||
if((ret = mbedtls_x509write_crt_pem(&server_cert, cert_buffer, sizeof(cert_buffer),
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
|
||||
{
|
||||
printf("ERROR: mbedtls_x509write_crt_pem returned %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write private key and certificate to file
|
||||
FILE *f = NULL;
|
||||
printf("Storing key + certificate in %s ...\n", certfile);
|
||||
if ((f = fopen(certfile, "wb")) == NULL)
|
||||
{
|
||||
printf("ERROR: Could not open %s for writing\n", certfile);
|
||||
return false;
|
||||
}
|
||||
// Create file with CA certificate only
|
||||
write_to_file(certfile, "CA certificate", "_ca.crt", (char*)ca_buffer, NULL);
|
||||
|
||||
// Write private key
|
||||
olen = strlen((char *) key_buffer);
|
||||
if (fwrite(key_buffer, 1, olen, f) != olen)
|
||||
{
|
||||
printf("ERROR: Could not write key to %s\n", certfile);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
// Create file with server certificate only
|
||||
write_to_file(certfile, "server certificate", ".crt", (char*)cert_buffer, NULL);
|
||||
|
||||
// Write certificate
|
||||
olen = strlen((char *) cert_buffer);
|
||||
if (fwrite(cert_buffer, 1, olen, f) != olen)
|
||||
{
|
||||
printf("ERROR: Could not write certificate to %s\n", certfile);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close key+cert file
|
||||
fclose(f);
|
||||
|
||||
// Create second file with certificate only
|
||||
char *certfile2 = calloc(strlen(certfile) + 5, sizeof(char));
|
||||
strcpy(certfile2, certfile);
|
||||
|
||||
// If the certificate file name ends with ".pem" or ".key", replace it with ".crt"
|
||||
// Otherwise, append ".crt" to the certificate file name
|
||||
if (strlen(certfile2) > 4 &&
|
||||
(strcmp(certfile2 + strlen(certfile2) - 4, ".pem") == 0 ||
|
||||
strcmp(certfile2 + strlen(certfile2) - 4, ".key") == 0))
|
||||
certfile2[strlen(certfile) - 4] = '\0';
|
||||
|
||||
strcat(certfile2, ".crt");
|
||||
|
||||
printf("Storing certificate in %s ...\n", certfile2);
|
||||
if ((f = fopen(certfile2, "wb")) == NULL)
|
||||
{
|
||||
printf("ERROR: Could not open %s for writing\n", certfile2);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write certificate
|
||||
olen = strlen((char *) cert_buffer);
|
||||
if (fwrite(cert_buffer, 1, olen, f) != olen)
|
||||
{
|
||||
printf("ERROR: Could not write certificate to %s\n", certfile2);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close cert file
|
||||
fclose(f);
|
||||
free(certfile2);
|
||||
// Write server's private key and certificate to file
|
||||
write_to_file(certfile, "server key + certificate", NULL, (char*)cert_buffer, (char*)key_buffer);
|
||||
|
||||
// Free resources
|
||||
mbedtls_x509write_crt_free(&crt);
|
||||
mbedtls_pk_free(&key);
|
||||
mbedtls_x509write_crt_free(&ca_cert);
|
||||
mbedtls_x509write_crt_free(&server_cert);
|
||||
mbedtls_pk_free(&ca_key);
|
||||
mbedtls_pk_free(&server_key);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
|
||||
|
@ -345,11 +387,12 @@ enum cert_check read_certificate(const char* certfile, const char *domain, const
|
|||
return CERT_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool has_key = true;
|
||||
int rc = mbedtls_pk_parse_keyfile(&key, certfile, NULL, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
if (rc != 0)
|
||||
{
|
||||
log_err("Cannot parse key: Error code %d", rc);
|
||||
return CERT_CANNOT_PARSE_KEY;
|
||||
log_info("No key found");
|
||||
has_key = false;
|
||||
}
|
||||
|
||||
rc = mbedtls_x509_crt_parse_file(&crt, certfile);
|
||||
|
@ -444,7 +487,7 @@ next_san:
|
|||
puts("Certificate (X.509):\n");
|
||||
puts(certinfo);
|
||||
|
||||
if(!private_key)
|
||||
if(!private_key || !has_key)
|
||||
goto end;
|
||||
|
||||
puts("Private key:");
|
||||
|
|
|
@ -64,7 +64,7 @@ CREATE TABLE info
|
|||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "info" VALUES('version','12');
|
||||
INSERT INTO "info" VALUES('version','19');
|
||||
|
||||
CREATE TABLE domainlist_by_group
|
||||
(
|
||||
|
@ -136,14 +136,14 @@ CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist
|
|||
AND domainlist.type = 3
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
CREATE VIEW vw_gravity AS SELECT domain, adlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_gravity AS SELECT domain, adlist.id AS adlist_id, adlist_by_group.group_id AS group_id
|
||||
FROM gravity
|
||||
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = gravity.adlist_id
|
||||
LEFT JOIN adlist ON adlist.id = gravity.adlist_id
|
||||
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
|
||||
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1) AND adlist.type = 0;
|
||||
|
||||
CREATE VIEW vw_antigravity AS SELECT domain, adlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_antigravity AS SELECT domain, adlist.id AS adlist_id, adlist_by_group.group_id AS group_id
|
||||
FROM antigravity
|
||||
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = antigravity.adlist_id
|
||||
LEFT JOIN adlist ON adlist.id = antigravity.adlist_id
|
||||
|
@ -180,17 +180,17 @@ CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
|
|||
INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Default');
|
||||
END;
|
||||
|
||||
CREATE TRIGGER tr_domainlist_delete AFTER DELETE ON domainlist
|
||||
CREATE TRIGGER tr_domainlist_delete BEFORE DELETE ON domainlist
|
||||
BEGIN
|
||||
DELETE FROM domainlist_by_group WHERE domainlist_id = OLD.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER tr_adlist_delete AFTER DELETE ON adlist
|
||||
CREATE TRIGGER tr_adlist_delete BEFORE DELETE ON adlist
|
||||
BEGIN
|
||||
DELETE FROM adlist_by_group WHERE adlist_id = OLD.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER tr_client_delete AFTER DELETE ON client
|
||||
CREATE TRIGGER tr_client_delete BEFORE DELETE ON client
|
||||
BEGIN
|
||||
DELETE FROM client_by_group WHERE client_id = OLD.id;
|
||||
END;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIB+jCCAVmgAwIBAgIPMDY1NTgwNDA4Mjk5OTE2MAwGCCqGSM49BAMCBQAwEjEQ
|
||||
MA4GA1UEAwwHcGkuaG9sZTAeFw0wMTAxMDEwMDAwMDBaFw0zMDEyMzEyMzU5NTla
|
||||
MBIxEDAOBgNVBAMMB3BpLmhvbGUwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAFD
|
||||
nUd44uNJqnVev6mcVAmq8Flz3ZNfIvgrAhl2mweX3k9zRdyfxfPKjHRxaEzLJxCB
|
||||
wZsjDee558Jlpd9zcK5TfQE8N1v3WH6sOFXj5WQSQA08FuApDqQKIc20wB26DJp4
|
||||
fHMWmp6AYa+BDaXhWn3yXgzvMEIbor9FtkOUO81QKDASUaNNMEswCQYDVR0TBAIw
|
||||
ADAdBgNVHQ4EFgQUwoMB6ZJW7JkdwmCy2Hp6sP0dkEAwHwYDVR0jBBgwFoAUwoMB
|
||||
6ZJW7JkdwmCy2Hp6sP0dkEAwDAYIKoZIzj0EAwIFAAOBjAAwgYgCQgEj2ykySK/P
|
||||
gbyT+J+vXVMBWbdHudfkncM7ItPhMN1PyM1J0Tp5emXm6ZLtlZpNGgqXxH1U94UO
|
||||
5AFs5PeJuLI43AJCAZJAEiEHqEycXxCm3Ip+64a7lb5H6Y3gpbUKjHwsZW4svTdk
|
||||
vn5eqsRcmuhW7t0pYJcpGGE52tV+Ayo8BQOLHJzd
|
||||
MIIB4TCCAWagAwIBAgIPNjYyMjUxNzYwMDkxMDA3MAoGCCqGSM49BAMCMDExEDAO
|
||||
BgNVBAMMB3BpLmhvbGUxEDAOBgNVBAoMB1BpLWhvbGUxCzAJBgNVBAYTAkRFMCAX
|
||||
DTIzMDExNjIxMTUxMloYDzIwNTMwMTE2MjExNTEyWjASMRAwDgYDVQQDDAdwaS5o
|
||||
b2xlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuH7sWfGRkvm5s5LVYTwbM6PjZmuK
|
||||
4KPhA5qaWfVqJw4jeEMkvyT4CKtiruLEBcqzimkBhP6dlMOUM/K0caRC5Jm46fMC
|
||||
9bV374ibYXxiX4bkiu8m/GDjM5RgiS1D1x+Uo2EwXzAdBgNVHQ4EFgQUh4lGwfX0
|
||||
GfdLVzkkHCuoxDkdiYkwHwYDVR0jBBgwFoAUUeMrZ6L+iRSicUSYbomqc/gPaikw
|
||||
CQYDVR0TBAIwADASBgNVHREECzAJggdwaS5ob2xlMAoGCCqGSM49BAMCA2kAMGYC
|
||||
MQDalH2DB1QTs5T3Vr4Ok+k+9E2xE2eHowMow5jHhYqmxW0jUqeNO1GWVyKQydmH
|
||||
sbQCMQDlcnMv4G3at02j/E7RBU67mRYL+DE5k0ygX1ANFYMZKrTM0960uoo8/DUl
|
||||
3cdeFrg=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIALL5s+KkTtEXyERZbBHO3A3tbBhh8hoWu9KWDVMcGHDiBc+CwA3Sl
|
||||
XOrHu1iGFZydVLPAIFZDVaD6caVVWTBBVtigBwYFK4EEACOhgYkDgYYABAFDnUd4
|
||||
4uNJqnVev6mcVAmq8Flz3ZNfIvgrAhl2mweX3k9zRdyfxfPKjHRxaEzLJxCBwZsj
|
||||
Dee558Jlpd9zcK5TfQE8N1v3WH6sOFXj5WQSQA08FuApDqQKIc20wB26DJp4fHMW
|
||||
mp6AYa+BDaXhWn3yXgzvMEIbor9FtkOUO81QKDASUQ==
|
||||
MIGkAgEBBDBGWIbQ11v8sQjrlj+KUS7OJoR0M9xyZyMLhkejtXlHGNXn2lK8ZzPW
|
||||
UUA6+ZqgdA+gBwYFK4EEACKhZANiAAS4fuxZ8ZGS+bmzktVhPBszo+Nma4rgo+ED
|
||||
mppZ9WonDiN4QyS/JPgIq2Ku4sQFyrOKaQGE/p2Uw5Qz8rRxpELkmbjp8wL1tXfv
|
||||
iJthfGJfhuSK7yb8YOMzlGCJLUPXH5Q=
|
||||
-----END EC PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB+jCCAVmgAwIBAgIPMDY1NTgwNDA4Mjk5OTE2MAwGCCqGSM49BAMCBQAwEjEQ
|
||||
MA4GA1UEAwwHcGkuaG9sZTAeFw0wMTAxMDEwMDAwMDBaFw0zMDEyMzEyMzU5NTla
|
||||
MBIxEDAOBgNVBAMMB3BpLmhvbGUwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAFD
|
||||
nUd44uNJqnVev6mcVAmq8Flz3ZNfIvgrAhl2mweX3k9zRdyfxfPKjHRxaEzLJxCB
|
||||
wZsjDee558Jlpd9zcK5TfQE8N1v3WH6sOFXj5WQSQA08FuApDqQKIc20wB26DJp4
|
||||
fHMWmp6AYa+BDaXhWn3yXgzvMEIbor9FtkOUO81QKDASUaNNMEswCQYDVR0TBAIw
|
||||
ADAdBgNVHQ4EFgQUwoMB6ZJW7JkdwmCy2Hp6sP0dkEAwHwYDVR0jBBgwFoAUwoMB
|
||||
6ZJW7JkdwmCy2Hp6sP0dkEAwDAYIKoZIzj0EAwIFAAOBjAAwgYgCQgEj2ykySK/P
|
||||
gbyT+J+vXVMBWbdHudfkncM7ItPhMN1PyM1J0Tp5emXm6ZLtlZpNGgqXxH1U94UO
|
||||
5AFs5PeJuLI43AJCAZJAEiEHqEycXxCm3Ip+64a7lb5H6Y3gpbUKjHwsZW4svTdk
|
||||
vn5eqsRcmuhW7t0pYJcpGGE52tV+Ayo8BQOLHJzd
|
||||
MIIB4TCCAWagAwIBAgIPNjYyMjUxNzYwMDkxMDA3MAoGCCqGSM49BAMCMDExEDAO
|
||||
BgNVBAMMB3BpLmhvbGUxEDAOBgNVBAoMB1BpLWhvbGUxCzAJBgNVBAYTAkRFMCAX
|
||||
DTIzMDExNjIxMTUxMloYDzIwNTMwMTE2MjExNTEyWjASMRAwDgYDVQQDDAdwaS5o
|
||||
b2xlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuH7sWfGRkvm5s5LVYTwbM6PjZmuK
|
||||
4KPhA5qaWfVqJw4jeEMkvyT4CKtiruLEBcqzimkBhP6dlMOUM/K0caRC5Jm46fMC
|
||||
9bV374ibYXxiX4bkiu8m/GDjM5RgiS1D1x+Uo2EwXzAdBgNVHQ4EFgQUh4lGwfX0
|
||||
GfdLVzkkHCuoxDkdiYkwHwYDVR0jBBgwFoAUUeMrZ6L+iRSicUSYbomqc/gPaikw
|
||||
CQYDVR0TBAIwADASBgNVHREECzAJggdwaS5ob2xlMAoGCCqGSM49BAMCA2kAMGYC
|
||||
MQDalH2DB1QTs5T3Vr4Ok+k+9E2xE2eHowMow5jHhYqmxW0jUqeNO1GWVyKQydmH
|
||||
sbQCMQDlcnMv4G3at02j/E7RBU67mRYL+DE5k0ygX1ANFYMZKrTM0960uoo8/DUl
|
||||
3cdeFrg=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIB8TCCAXegAwIBAgIPMDQ4MzEwMzI5NDMzNTEyMAoGCCqGSM49BAMCMDExEDAO
|
||||
BgNVBAMMB3BpLmhvbGUxEDAOBgNVBAoMB1BpLWhvbGUxCzAJBgNVBAYTAkRFMCAX
|
||||
DTIzMDExNjIxMTUxMloYDzIwNTMwMTE2MjExNTEyWjAxMRAwDgYDVQQDDAdwaS5o
|
||||
b2xlMRAwDgYDVQQKDAdQaS1ob2xlMQswCQYDVQQGEwJERTB2MBAGByqGSM49AgEG
|
||||
BSuBBAAiA2IABJLGB8r6Fs40ARHd8OtOIfAjAyW07QMO7LRlS/M2lJhXWUoPMn3W
|
||||
KsGhfOPB24aJZ/aFjZRSBnlgiBTten/SJIQ8ooSVLHLZXKHMDwAqjLA7woMb2kOC
|
||||
QiqHRnKdY3xH2KNTMFEwHQYDVR0OBBYEFFHjK2ei/okUonFEmG6JqnP4D2opMB8G
|
||||
A1UdIwQYMBaAFFHjK2ei/okUonFEmG6JqnP4D2opMA8GA1UdEwEB/wQFMAMBAf8w
|
||||
CgYIKoZIzj0EAwIDaAAwZQIxAJAP3Hx3d6WWys1JiV68CeGL8+hngTmEkzcPivyC
|
||||
YqkJR8Ni7TBvhtYViyccQ6g3LAIwIsbtRFStxJYuRDi9tTwHuOKZby/oYUwRXMff
|
||||
7XmiEM85caDyY4rG6oN910Cm+v8c
|
||||
-----END CERTIFICATE-----
|
|
@ -190,7 +190,7 @@
|
|||
run bash -c "grep -c 'get_client_querystr: SELECT id from vw_blacklist WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} != "0" ]]
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT domain from vw_gravity WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT adlist_id from vw_gravity WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} != "0" ]]
|
||||
run bash -c "grep -c 'Regex allow ([[:digit:]]*, DB ID [[:digit:]]*) .* NOT ENABLED for client 127.0.0.4' /var/log/pihole/FTL.log"
|
||||
|
@ -222,7 +222,7 @@
|
|||
run bash -c "grep -c 'get_client_querystr: SELECT id from vw_blacklist WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} != "0" ]]
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT domain from vw_gravity WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT adlist_id from vw_gravity WHERE domain = ? AND group_id IN (4);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} != "0" ]]
|
||||
run bash -c "grep -c 'Regex allow ([[:digit:]]*, DB ID [[:digit:]]*) .* NOT ENABLED for client 127.0.0.5' /var/log/pihole/FTL.log"
|
||||
|
@ -260,7 +260,7 @@
|
|||
run bash -c "grep -c 'get_client_querystr: SELECT id from vw_blacklist WHERE domain = ? AND group_id IN (5);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "1" ]]
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT domain from vw_gravity WHERE domain = ? AND group_id IN (5);' /var/log/pihole/FTL.log"
|
||||
run bash -c "grep -c 'get_client_querystr: SELECT adlist_id from vw_gravity WHERE domain = ? AND group_id IN (5);' /var/log/pihole/FTL.log"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "1" ]]
|
||||
run bash -c "grep -c 'Regex allow ([[:digit:]]*, DB ID [[:digit:]]*) .* NOT ENABLED for client 127.0.0.6' /var/log/pihole/FTL.log"
|
||||
|
@ -454,16 +454,16 @@
|
|||
@test "pihole-FTL.db schema is as expected" {
|
||||
run bash -c './pihole-FTL sqlite3 /etc/pihole/pihole-FTL.db .dump'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ "${lines[@]}" == *"CREATE TABLE IF NOT EXISTS \"query_storage\" (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain INTEGER NOT NULL, client INTEGER NOT NULL, forward INTEGER, additional_info INTEGER, reply_type INTEGER, reply_time REAL, dnssec INTEGER, regex_id INTEGER);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE IF NOT EXISTS \"query_storage\" (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain INTEGER NOT NULL, client INTEGER NOT NULL, forward INTEGER, additional_info INTEGER, reply_type INTEGER, reply_time REAL, dnssec INTEGER, list_id INTEGER);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE INDEX idx_queries_timestamps ON \"query_storage\" (timestamp);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE ftl (id INTEGER PRIMARY KEY NOT NULL, value BLOB NOT NULL, description TEXT);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE counters (id INTEGER PRIMARY KEY NOT NULL, value INTEGER NOT NULL);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE IF NOT EXISTS \"network\" (id INTEGER PRIMARY KEY NOT NULL, hwaddr TEXT UNIQUE NOT NULL, interface TEXT NOT NULL, firstSeen INTEGER NOT NULL, lastQuery INTEGER NOT NULL, numQueries INTEGER NOT NULL, macVendor TEXT, aliasclient_id INTEGER);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE IF NOT EXISTS \"network_addresses\" (network_id INTEGER NOT NULL, ip TEXT UNIQUE NOT NULL, lastSeen INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), name TEXT, nameUpdated INTEGER, FOREIGN KEY(network_id) REFERENCES network(id));"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE aliasclient (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, comment TEXT);"* ]]
|
||||
[[ "${lines[@]}" == *"INSERT INTO ftl VALUES(0,16,'Database version');"* ]]
|
||||
[[ "${lines[@]}" == *"INSERT INTO ftl VALUES(0,17,'Database version');"* ]]
|
||||
# vvv This has been added in version 10 vvv
|
||||
[[ "${lines[@]}" == *"CREATE VIEW queries AS SELECT id, timestamp, type, status, CASE typeof(domain) WHEN 'integer' THEN (SELECT domain FROM domain_by_id d WHERE d.id = q.domain) ELSE domain END domain,CASE typeof(client) WHEN 'integer' THEN (SELECT ip FROM client_by_id c WHERE c.id = q.client) ELSE client END client,CASE typeof(forward) WHEN 'integer' THEN (SELECT forward FROM forward_by_id f WHERE f.id = q.forward) ELSE forward END forward,CASE typeof(additional_info) WHEN 'integer' THEN (SELECT content FROM addinfo_by_id a WHERE a.id = q.additional_info) ELSE additional_info END additional_info, reply_type, reply_time, dnssec, regex_id FROM query_storage q;"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE VIEW queries AS SELECT id, timestamp, type, status, CASE typeof(domain) WHEN 'integer' THEN (SELECT domain FROM domain_by_id d WHERE d.id = q.domain) ELSE domain END domain,CASE typeof(client) WHEN 'integer' THEN (SELECT ip FROM client_by_id c WHERE c.id = q.client) ELSE client END client,CASE typeof(forward) WHEN 'integer' THEN (SELECT forward FROM forward_by_id f WHERE f.id = q.forward) ELSE forward END forward,CASE typeof(additional_info) WHEN 'integer' THEN (SELECT content FROM addinfo_by_id a WHERE a.id = q.additional_info) ELSE additional_info END additional_info, reply_type, reply_time, dnssec, list_id FROM query_storage q;"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE domain_by_id (id INTEGER PRIMARY KEY, domain TEXT NOT NULL);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE client_by_id (id INTEGER PRIMARY KEY, ip TEXT NOT NULL, name TEXT);"* ]]
|
||||
[[ "${lines[@]}" == *"CREATE TABLE forward_by_id (id INTEGER PRIMARY KEY, forward TEXT NOT NULL);"* ]]
|
||||
|
@ -1455,22 +1455,23 @@
|
|||
[[ "${lines[0]}" == "Reading certificate from /etc/pihole/test.pem ..." ]]
|
||||
[[ "${lines[1]}" == "Certificate (X.509):" ]]
|
||||
[[ "${lines[2]}" == " cert. version : 3" ]]
|
||||
[[ "${lines[3]}" == " serial number : 30:36:35:35:38:30:34:30:38:32:39:39:39:31:36" ]]
|
||||
[[ "${lines[4]}" == " issuer name : CN=pi.hole" ]]
|
||||
[[ "${lines[3]}" == " serial number : 36:36:32:32:35:31:37:36:30:30:39:31:30:30:37" ]]
|
||||
[[ "${lines[4]}" == " issuer name : CN=pi.hole, O=Pi-hole, C=DE" ]]
|
||||
[[ "${lines[5]}" == " subject name : CN=pi.hole" ]]
|
||||
[[ "${lines[6]}" == " issued on : 2001-01-01 00:00:00" ]]
|
||||
[[ "${lines[7]}" == " expires on : 2030-12-31 23:59:59" ]]
|
||||
[[ "${lines[6]}" == " issued on : 2023-01-16 21:15:12" ]]
|
||||
[[ "${lines[7]}" == " expires on : 2053-01-16 21:15:12" ]]
|
||||
[[ "${lines[8]}" == " signed using : ECDSA with SHA256" ]]
|
||||
[[ "${lines[9]}" == " EC key size : 521 bits" ]]
|
||||
[[ "${lines[9]}" == " EC key size : 384 bits" ]]
|
||||
[[ "${lines[10]}" == " basic constraints : CA=false" ]]
|
||||
[[ "${lines[11]}" == "Public key (PEM):" ]]
|
||||
[[ "${lines[12]}" == "-----BEGIN PUBLIC KEY-----" ]]
|
||||
[[ "${lines[13]}" == "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBQ51HeOLjSap1Xr+pnFQJqvBZc92T" ]]
|
||||
[[ "${lines[14]}" == "XyL4KwIZdpsHl95Pc0Xcn8Xzyox0cWhMyycQgcGbIw3nuefCZaXfc3CuU30BPDdb" ]]
|
||||
[[ "${lines[15]}" == "91h+rDhV4+VkEkANPBbgKQ6kCiHNtMAdugyaeHxzFpqegGGvgQ2l4Vp98l4M7zBC" ]]
|
||||
[[ "${lines[16]}" == "G6K/RbZDlDvNUCgwElE=" ]]
|
||||
[[ "${lines[17]}" == "-----END PUBLIC KEY-----" ]]
|
||||
[[ "${lines[18]}" == "" ]]
|
||||
[[ "${lines[11]}" == " subject alt name :" ]]
|
||||
[[ "${lines[12]}" == " dNSName : pi.hole" ]]
|
||||
[[ "${lines[13]}" == "Public key (PEM):" ]]
|
||||
[[ "${lines[14]}" == "-----BEGIN PUBLIC KEY-----" ]]
|
||||
[[ "${lines[15]}" == "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuH7sWfGRkvm5s5LVYTwbM6PjZmuK4KPh" ]]
|
||||
[[ "${lines[16]}" == "A5qaWfVqJw4jeEMkvyT4CKtiruLEBcqzimkBhP6dlMOUM/K0caRC5Jm46fMC9bV3" ]]
|
||||
[[ "${lines[17]}" == "74ibYXxiX4bkiu8m/GDjM5RgiS1D1x+U" ]]
|
||||
[[ "${lines[18]}" == "-----END PUBLIC KEY-----" ]]
|
||||
[[ "${lines[19]}" == "" ]]
|
||||
}
|
||||
|
||||
@test "X.509 certificate parser returns expected result (with private key)" {
|
||||
|
@ -1480,38 +1481,38 @@
|
|||
[[ "${lines[0]}" == "Reading certificate from /etc/pihole/test.pem ..." ]]
|
||||
[[ "${lines[1]}" == "Certificate (X.509):" ]]
|
||||
[[ "${lines[2]}" == " cert. version : 3" ]]
|
||||
[[ "${lines[3]}" == " serial number : 30:36:35:35:38:30:34:30:38:32:39:39:39:31:36" ]]
|
||||
[[ "${lines[4]}" == " issuer name : CN=pi.hole" ]]
|
||||
[[ "${lines[3]}" == " serial number : 36:36:32:32:35:31:37:36:30:30:39:31:30:30:37" ]]
|
||||
[[ "${lines[4]}" == " issuer name : CN=pi.hole, O=Pi-hole, C=DE" ]]
|
||||
[[ "${lines[5]}" == " subject name : CN=pi.hole" ]]
|
||||
[[ "${lines[6]}" == " issued on : 2001-01-01 00:00:00" ]]
|
||||
[[ "${lines[7]}" == " expires on : 2030-12-31 23:59:59" ]]
|
||||
[[ "${lines[6]}" == " issued on : 2023-01-16 21:15:12" ]]
|
||||
[[ "${lines[7]}" == " expires on : 2053-01-16 21:15:12" ]]
|
||||
[[ "${lines[8]}" == " signed using : ECDSA with SHA256" ]]
|
||||
[[ "${lines[9]}" == " EC key size : 521 bits" ]]
|
||||
[[ "${lines[9]}" == " EC key size : 384 bits" ]]
|
||||
[[ "${lines[10]}" == " basic constraints : CA=false" ]]
|
||||
[[ "${lines[11]}" == "Private key:" ]]
|
||||
[[ "${lines[12]}" == " Type: EC" ]]
|
||||
[[ "${lines[13]}" == " Curve type: Short Weierstrass (y^2 = x^3 + a x + b)" ]]
|
||||
[[ "${lines[14]}" == " Bitlen: 518 bit" ]]
|
||||
[[ "${lines[15]}" == " Private key:" ]]
|
||||
[[ "${lines[16]}" == " D = 0x2CBE6CF8A913B445F211165B0473B7037B5B06187C8685AEF4A58354C7061C388173E0B00374A55CEAC7BB5886159C9D54B3C020564355A0FA71A55559304156D8"* ]]
|
||||
[[ "${lines[17]}" == " Public key:" ]]
|
||||
[[ "${lines[18]}" == " X = 0x01439D4778E2E349AA755EBFA99C5409AAF05973DD935F22F82B0219769B0797DE4F7345DC9FC5F3CA8C7471684CCB271081C19B230DE7B9E7C265A5DF7370AE537D"* ]]
|
||||
[[ "${lines[19]}" == " Y = 0x013C375BF7587EAC3855E3E56412400D3C16E0290EA40A21CDB4C01DBA0C9A787C73169A9E8061AF810DA5E15A7DF25E0CEF30421BA2BF45B643943BCD5028301251"* ]]
|
||||
[[ "${lines[20]}" == " Z = 0x01"* ]]
|
||||
[[ "${lines[21]}" == "Private key (PEM):" ]]
|
||||
[[ "${lines[22]}" == "-----BEGIN EC PRIVATE KEY-----" ]]
|
||||
[[ "${lines[23]}" == "MIHcAgEBBEIALL5s+KkTtEXyERZbBHO3A3tbBhh8hoWu9KWDVMcGHDiBc+CwA3Sl" ]]
|
||||
[[ "${lines[24]}" == "XOrHu1iGFZydVLPAIFZDVaD6caVVWTBBVtigBwYFK4EEACOhgYkDgYYABAFDnUd4" ]]
|
||||
[[ "${lines[25]}" == "4uNJqnVev6mcVAmq8Flz3ZNfIvgrAhl2mweX3k9zRdyfxfPKjHRxaEzLJxCBwZsj" ]]
|
||||
[[ "${lines[26]}" == "Dee558Jlpd9zcK5TfQE8N1v3WH6sOFXj5WQSQA08FuApDqQKIc20wB26DJp4fHMW" ]]
|
||||
[[ "${lines[27]}" == "mp6AYa+BDaXhWn3yXgzvMEIbor9FtkOUO81QKDASUQ==" ]]
|
||||
[[ "${lines[28]}" == "-----END EC PRIVATE KEY-----" ]]
|
||||
[[ "${lines[29]}" == "Public key (PEM):" ]]
|
||||
[[ "${lines[30]}" == "-----BEGIN PUBLIC KEY-----" ]]
|
||||
[[ "${lines[31]}" == "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBQ51HeOLjSap1Xr+pnFQJqvBZc92T" ]]
|
||||
[[ "${lines[32]}" == "XyL4KwIZdpsHl95Pc0Xcn8Xzyox0cWhMyycQgcGbIw3nuefCZaXfc3CuU30BPDdb" ]]
|
||||
[[ "${lines[33]}" == "91h+rDhV4+VkEkANPBbgKQ6kCiHNtMAdugyaeHxzFpqegGGvgQ2l4Vp98l4M7zBC" ]]
|
||||
[[ "${lines[34]}" == "G6K/RbZDlDvNUCgwElE=" ]]
|
||||
[[ "${lines[11]}" == " subject alt name :" ]]
|
||||
[[ "${lines[12]}" == " dNSName : pi.hole" ]]
|
||||
[[ "${lines[13]}" == "Private key:" ]]
|
||||
[[ "${lines[14]}" == " Type: EC" ]]
|
||||
[[ "${lines[15]}" == " Curve type: Short Weierstrass (y^2 = x^3 + a x + b)" ]]
|
||||
[[ "${lines[16]}" == " Bitlen: 383 bit" ]]
|
||||
[[ "${lines[17]}" == " Private key:" ]]
|
||||
[[ "${lines[18]}" == " D = 0x465886D0D75BFCB108EB963F8A512ECE26847433DC7267230B8647A3B5794718D5E7DA52BC6733D651403AF99AA0740F"* ]]
|
||||
[[ "${lines[19]}" == " Public key:" ]]
|
||||
[[ "${lines[20]}" == " X = 0xB87EEC59F19192F9B9B392D5613C1B33A3E3666B8AE0A3E1039A9A59F56A270E23784324BF24F808AB62AEE2C405CAB3"* ]]
|
||||
[[ "${lines[21]}" == " Y = 0x8A690184FE9D94C39433F2B471A442E499B8E9F302F5B577EF889B617C625F86E48AEF26FC60E3339460892D43D71F94"* ]]
|
||||
[[ "${lines[22]}" == " Z = 0x01"* ]]
|
||||
[[ "${lines[23]}" == "Private key (PEM):" ]]
|
||||
[[ "${lines[24]}" == "-----BEGIN EC PRIVATE KEY-----" ]]
|
||||
[[ "${lines[25]}" == "MIGkAgEBBDBGWIbQ11v8sQjrlj+KUS7OJoR0M9xyZyMLhkejtXlHGNXn2lK8ZzPW" ]]
|
||||
[[ "${lines[26]}" == "UUA6+ZqgdA+gBwYFK4EEACKhZANiAAS4fuxZ8ZGS+bmzktVhPBszo+Nma4rgo+ED" ]]
|
||||
[[ "${lines[27]}" == "mppZ9WonDiN4QyS/JPgIq2Ku4sQFyrOKaQGE/p2Uw5Qz8rRxpELkmbjp8wL1tXfv" ]]
|
||||
[[ "${lines[28]}" == "iJthfGJfhuSK7yb8YOMzlGCJLUPXH5Q=" ]]
|
||||
[[ "${lines[29]}" == "-----END EC PRIVATE KEY-----" ]]
|
||||
[[ "${lines[30]}" == "Public key (PEM):" ]]
|
||||
[[ "${lines[31]}" == "-----BEGIN PUBLIC KEY-----" ]]
|
||||
[[ "${lines[32]}" == "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuH7sWfGRkvm5s5LVYTwbM6PjZmuK4KPh" ]]
|
||||
[[ "${lines[33]}" == "A5qaWfVqJw4jeEMkvyT4CKtiruLEBcqzimkBhP6dlMOUM/K0caRC5Jm46fMC9bV3" ]]
|
||||
[[ "${lines[34]}" == "74ibYXxiX4bkiu8m/GDjM5RgiS1D1x+U" ]]
|
||||
[[ "${lines[35]}" == "-----END PUBLIC KEY-----" ]]
|
||||
[[ "${lines[36]}" == "" ]]
|
||||
}
|
||||
|
@ -1567,7 +1568,16 @@
|
|||
@test "SHA256 checksum working" {
|
||||
run bash -c './pihole-FTL sha256sum test/test.pem'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "eae293f0c30369935a7457a789658bedebf92d544e7526bc43aa07883a597fa9 test/test.pem" ]]
|
||||
[[ ${lines[0]} == "ce4c01340ef46bf3bc26831f7c53763d57c863528826aa795f1da5e16d6e7b2d test/test.pem" ]]
|
||||
}
|
||||
|
||||
@test "Internal IP -> name resolution works" {
|
||||
run bash -c "./pihole-FTL ptr 127.0.0.1 | tail -n1"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "localhost" ]]
|
||||
run bash -c "./pihole-FTL ptr ::1 | tail -n1"
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "localhost" ]]
|
||||
}
|
||||
|
||||
@test "API validation" {
|
||||
|
|
Loading…
Reference in New Issue