Merge pull request #1771 from pi-hole/tweak/clients_mac_hostnames
/api/client: Include hostnames of clients specified by MAC
This commit is contained in:
commit
12e8bd773f
|
@ -58,9 +58,11 @@ static int api_list_read(struct ftl_conn *api,
|
|||
char *name = NULL;
|
||||
if(table.client != NULL)
|
||||
{
|
||||
// Try to obtain hostname if this is a valid IP address
|
||||
// Try to obtain hostname
|
||||
if(isValidIPv4(table.client) || isValidIPv6(table.client))
|
||||
name = getNameFromIP(NULL, table.client);
|
||||
else if(isMAC(table.client))
|
||||
name = getNameFromMAC(table.client);
|
||||
}
|
||||
|
||||
JSON_COPY_STR_TO_OBJECT(row, "client", table.client);
|
||||
|
|
|
@ -2144,6 +2144,84 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
|
|||
return name;
|
||||
}
|
||||
|
||||
// Get most recently seen host name of device identified by MAC address
|
||||
char *__attribute__((malloc)) getNameFromMAC(const char *client)
|
||||
{
|
||||
// Return early if database is known to be broken
|
||||
if(FTLDBerror())
|
||||
return NULL;
|
||||
|
||||
log_debug(DEBUG_DATABASE,"Looking up host name for %s", client);
|
||||
|
||||
// Open pihole-FTL.db database file
|
||||
sqlite3 *db = NULL;
|
||||
if((db = dbopen(false, false)) == NULL)
|
||||
{
|
||||
log_warn("getNameFromMAC(\"%s\") - Failed to open DB", client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check for a host name associated with the given client as MAC address
|
||||
// COLLATE NOCASE: Case-insensitive comparison
|
||||
const char *querystr = "SELECT name FROM network_addresses "
|
||||
"WHERE name IS NOT NULL AND "
|
||||
"network_id = (SELECT id FROM network WHERE hwaddr = ? COLLATE NOCASE) "
|
||||
"ORDER BY lastSeen DESC LIMIT 1";
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
|
||||
if(rc != SQLITE_OK)
|
||||
{
|
||||
log_err("getNameFromMAC(\"%s\") - SQL error prepare: %s",
|
||||
client, sqlite3_errstr(rc));
|
||||
dbclose(&db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Bind client to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 1, client, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
log_warn("getNameFromMAC(\"%s\"): Failed to bind ip: %s",
|
||||
client, sqlite3_errstr(rc));
|
||||
checkFTLDBrc(rc);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
dbclose(&db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *name = NULL;
|
||||
rc = sqlite3_step(stmt);
|
||||
if(rc == SQLITE_ROW)
|
||||
{
|
||||
// Database record found (result might be empty)
|
||||
name = strdup((char*)sqlite3_column_text(stmt, 0));
|
||||
|
||||
if(config.debug.resolver.v.b)
|
||||
log_debug(DEBUG_RESOLVER, "Found database host name (by MAC) %s -> %s",
|
||||
client, name);
|
||||
}
|
||||
else if(rc == SQLITE_DONE)
|
||||
{
|
||||
// Not found
|
||||
if(config.debug.resolver.v.b)
|
||||
log_debug(DEBUG_RESOLVER, " ---> not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error
|
||||
checkFTLDBrc(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finalize statement and close database handle
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
dbclose(&db);
|
||||
return name;
|
||||
}
|
||||
|
||||
// Get interface of device identified by IP address
|
||||
char *__attribute__((malloc)) getIfaceFromIP(sqlite3 *db, const char *ipaddr)
|
||||
{
|
||||
|
@ -2425,3 +2503,29 @@ bool networkTable_deleteDevice(sqlite3 *db, const int id, const char **message)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Counting number of occurrences of a specific char in a string
|
||||
static size_t __attribute__ ((pure)) count_char(const char *haystack, const char needle)
|
||||
{
|
||||
size_t count = 0u;
|
||||
while(*haystack)
|
||||
if (*haystack++ == needle)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Identify MAC addresses using a set of suitable criteria
|
||||
bool __attribute__ ((pure)) isMAC(const char *input)
|
||||
{
|
||||
if(input != NULL && // Valid input
|
||||
strlen(input) == 17u && // MAC addresses are always 17 chars long (6 bytes + 5 colons)
|
||||
count_char(input, ':') == 5u && // MAC addresses always have 5 colons
|
||||
strstr(input, "::") == NULL) // No double-colons (IPv6 address abbreviation)
|
||||
{
|
||||
// This is a MAC address of the form AA:BB:CC:DD:EE:FF
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not a MAC address
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@ bool create_network_addresses_with_names_table(sqlite3 *db);
|
|||
void parse_neighbor_cache(sqlite3 *db);
|
||||
void updateMACVendorRecords(sqlite3 *db);
|
||||
bool unify_hwaddr(sqlite3 *db);
|
||||
char* __attribute__((malloc)) getMACfromIP(sqlite3 *db, const char* ipaddr);
|
||||
char *getMACfromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
|
||||
int getAliasclientIDfromIP(sqlite3 *db, const char *ipaddr);
|
||||
char* __attribute__((malloc)) getNameFromIP(sqlite3 *db, const char* ipaddr);
|
||||
char* __attribute__((malloc)) getIfaceFromIP(sqlite3 *db, const char* ipaddr);
|
||||
char *getNameFromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
|
||||
char *getNameFromMAC(const char *client) __attribute__((malloc));
|
||||
char *getIfaceFromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
|
||||
void resolveNetworkTableNames(void);
|
||||
bool flush_network_table(void);
|
||||
bool isMAC(const char *input) __attribute__ ((pure));
|
||||
|
||||
typedef struct {
|
||||
unsigned int id;
|
||||
|
|
|
@ -29,32 +29,6 @@
|
|||
// isMAC()
|
||||
#include "network-table.h"
|
||||
|
||||
// Counting number of occurrences of a specific char in a string
|
||||
static size_t __attribute__ ((pure)) count_char(const char *haystack, const char needle)
|
||||
{
|
||||
size_t count = 0u;
|
||||
while(*haystack)
|
||||
if (*haystack++ == needle)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Identify MAC addresses using a set of suitable criteria
|
||||
static bool __attribute__ ((pure)) isMAC(const char *input)
|
||||
{
|
||||
if(input != NULL && // Valid input
|
||||
strlen(input) == 17u && // MAC addresses are always 17 chars long (6 bytes + 5 colons)
|
||||
count_char(input, ':') == 5u && // MAC addresses always have 5 colons
|
||||
strstr(input, "::") == NULL) // No double-colons (IPv6 address abbreviation)
|
||||
{
|
||||
// This is a MAC address of the form AA:BB:CC:DD:EE:FF
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not a MAC address
|
||||
return false;
|
||||
}
|
||||
|
||||
static void subnet_match_impl(sqlite3_context *context, int argc, sqlite3_value **argv)
|
||||
{
|
||||
// Exactly two arguments should be submitted to this routine
|
||||
|
|
Loading…
Reference in New Issue