Implement proper error handling for network table IP address sub-query

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2020-06-17 19:18:09 +02:00
parent ae035c4ba7
commit 822f8c6a28
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
7 changed files with 157 additions and 115 deletions

View File

@ -14,6 +14,7 @@ set(sources
dns.c
dns.h
ftl.c
network.c
routes.c
routes.h
settings.c

View File

@ -12,15 +12,6 @@
#include "../webserver/http-common.h"
#include "../webserver/json_macros.h"
#include "routes.h"
#include "../datastructure.h"
// get_FTL_version()
#include "../log.h"
// git constants
#include "../version.h"
// config struct
#include "../config.h"
// networkrecord
#include "../database/network-table.h"
// struct fifologData
#include "../fifo.h"
@ -119,75 +110,3 @@ int api_ftl_dnsmasq_log(struct mg_connection *conn)
// Send data
JSON_SEND_OBJECT(json);
}
int api_ftl_network(struct mg_connection *conn)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn) < 0)
{
return send_json_unauthorized(conn);
}
// Connect to database
const char *sql_msg = NULL;
if(!networkTable_readDevices(&sql_msg))
{
// Add SQL message (may be NULL = not available)
cJSON *json = JSON_NEW_OBJ();
if (sql_msg != NULL) {
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
} else {
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
return send_json_error(conn, 500,
"database_error",
"Could not read network details from database table",
json);
}
// Read record for a single device
cJSON *json = JSON_NEW_ARRAY();
networkrecord network;
while(networkTable_readDevicesGetRecord(&network, &sql_msg))
{
cJSON *item = JSON_NEW_OBJ();
JSON_OBJ_COPY_STR(item, "hwaddr", network.hwaddr);
JSON_OBJ_COPY_STR(item, "interface", network.interface);
JSON_OBJ_COPY_STR(item, "name", network.name);
JSON_OBJ_ADD_NUMBER(item, "firstSeen", network.firstSeen);
JSON_OBJ_ADD_NUMBER(item, "lastQuery", network.lastQuery);
JSON_OBJ_ADD_NUMBER(item, "numQueries", network.numQueries);
JSON_OBJ_COPY_STR(item, "macVendor", network.macVendor);
// Build array of all IP addresses known associated to this client
cJSON *ip = JSON_NEW_ARRAY();
if(networkTable_readIPs(network.id))
{
// Only walk known IP addresses when SELECT query succeeded
const char *ipaddr;
while((ipaddr = networkTable_readIPsGetRecord()) != NULL)
JSON_ARRAY_COPY_STR(ip, ipaddr);
networkTable_readIPsFinalize();
}
JSON_OBJ_ADD_ITEM(item, "ip", ip);
JSON_ARRAY_ADD_ITEM(json, item);
}
if(sql_msg != NULL)
{
// Add SQL message (may be NULL = not available)
cJSON_Delete(json);
json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
return send_json_error(conn, 500,
"database_error",
"Could not read network details from database table (step)",
json);
}
networkTable_readDevicesFinalize();
JSON_SEND_OBJECT(json);
}

110
src/api/network.c Normal file
View File

@ -0,0 +1,110 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* API Implementation /api/network
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "../FTL.h"
#include "../webserver/http-common.h"
#include "../webserver/json_macros.h"
#include "routes.h"
// networkrecord
#include "../database/network-table.h"
int api_network(struct mg_connection *conn)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn) < 0)
{
return send_json_unauthorized(conn);
}
// Connect to database
const char *sql_msg = NULL;
if(!networkTable_readDevices(&sql_msg))
{
// Add SQL message (may be NULL = not available)
cJSON *json = JSON_NEW_OBJ();
if (sql_msg != NULL) {
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
} else {
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
return send_json_error(conn, 500,
"database_error",
"Could not read network details from database table",
json);
}
// Read record for a single device
cJSON *json = JSON_NEW_ARRAY();
network_record network;
while(networkTable_readDevicesGetRecord(&network, &sql_msg))
{
cJSON *item = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(item, "id", network.id);
JSON_OBJ_COPY_STR(item, "hwaddr", network.hwaddr);
JSON_OBJ_COPY_STR(item, "interface", network.iface);
JSON_OBJ_COPY_STR(item, "name", network.name);
JSON_OBJ_ADD_NUMBER(item, "firstSeen", network.firstSeen);
JSON_OBJ_ADD_NUMBER(item, "lastQuery", network.lastQuery);
JSON_OBJ_ADD_NUMBER(item, "numQueries", network.numQueries);
JSON_OBJ_COPY_STR(item, "macVendor", network.macVendor);
// Build array of all IP addresses known associated to this client
cJSON *ip = JSON_NEW_ARRAY();
if(networkTable_readIPs(network.id, &sql_msg))
{
// Walk known IP addresses
network_addresses_record network_address;
while(networkTable_readIPsGetRecord(&network_address, &sql_msg))
JSON_ARRAY_COPY_STR(ip, network_address.ip);
// Possible error handling
if(sql_msg != NULL)
{
cJSON_Delete(json);
json = JSON_NEW_OBJ();
// Add item leading to the error when generating the error message
JSON_OBJ_ADD_ITEM(json, "last_item", item);
// Add SQL message
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
return send_json_error(conn, 500,
"database_error",
"Could not read network details from database table (getting IP records)",
json);
}
// Finalize sub-query
networkTable_readIPsFinalize();
}
// Add array of IP addresses to device
JSON_OBJ_ADD_ITEM(item, "ip", ip);
// Add device to array of all devices
JSON_ARRAY_ADD_ITEM(json, item);
}
if(sql_msg != NULL)
{
cJSON_Delete(json);
json = JSON_NEW_OBJ();
// Add SQL message
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
return send_json_error(conn, 500,
"database_error",
"Could not read network details from database table (step)",
json);
}
// Finalize query
networkTable_readDevicesFinalize();
// Return data to user
JSON_SEND_OBJECT(json);
}

View File

@ -60,9 +60,10 @@ int api_handler(struct mg_connection *conn, void *ignored)
{
ret = api_ftl_dnsmasq_log(conn);
}
else if(startsWith("/api/ftl/network", request->local_uri))
/******************************** api/network ****************************/
else if(startsWith("/api/network", request->local_uri))
{
ret = api_ftl_network(conn);
ret = api_network(conn);
}
/******************************** api/stats **************************/
else if(startsWith("/api/stats/summary", request->local_uri))

View File

@ -38,7 +38,9 @@ int api_stats_database_upstreams(struct mg_connection *conn);
// FTL methods
int api_ftl_client(struct mg_connection *conn);
int api_ftl_dnsmasq_log(struct mg_connection *conn);
int api_ftl_network(struct mg_connection *conn);
// Network methods
int api_network(struct mg_connection *conn);
// DNS methods
int api_dns_blockingstatus(struct mg_connection *conn);

View File

@ -8,15 +8,16 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "FTL.h"
#include "database/network-table.h"
#include "database/common.h"
#include "shmem.h"
#include "memory.h"
#include "log.h"
#include "timers.h"
#include "config.h"
#include "datastructure.h"
#include "../FTL.h"
#include "network-table.h"
#include "common.h"
#include "../shmem.h"
#include "../memory.h"
#include "../log.h"
// timer_elapsed_msec()
#include "../timers.h"
#include "../config.h"
#include "../datastructure.h"
// Private prototypes
static char* getMACVendor(const char* hwaddr);
@ -966,7 +967,7 @@ bool networkTable_readDevices(const char **message)
return true;
}
bool networkTable_readDevicesGetRecord(networkrecord *network, const char **message)
bool networkTable_readDevicesGetRecord(network_record *network, const char **message)
{
// Perform step
const int rc = sqlite3_step(read_stmt);
@ -976,7 +977,7 @@ bool networkTable_readDevicesGetRecord(networkrecord *network, const char **mess
{
network->id = sqlite3_column_int(read_stmt, 0);
network->hwaddr = (char*)sqlite3_column_text(read_stmt, 1);
network->interface = (char*)sqlite3_column_text(read_stmt, 2);
network->iface = (char*)sqlite3_column_text(read_stmt, 2);
network->name = (char*)sqlite3_column_text(read_stmt, 3);
network->firstSeen = sqlite3_column_int(read_stmt, 4);
network->lastQuery = sqlite3_column_int(read_stmt, 5);
@ -1011,22 +1012,24 @@ void networkTable_readDevicesFinalize(void)
}
static sqlite3_stmt* read_stmt_ip = NULL;
bool networkTable_readIPs(const int id)
bool networkTable_readIPs(const int id, const char **message)
{
// Prepare SQLite statement
const char *querystr = "SELECT ip FROM network_addresses WHERE network_id = ? ORDER BY lastSeen DESC;";
int rc = sqlite3_prepare_v2(FTL_db, querystr, -1, &read_stmt_ip, NULL);
if( rc != SQLITE_OK ){
*message = sqlite3_errmsg(FTL_db);
logg("networkTable_readIPs(%i) - SQL error prepare (%i): %s",
id, rc, sqlite3_errmsg(FTL_db));
id, rc, *message);
return false;
}
// Bind ipaddr to prepared statement
if((rc = sqlite3_bind_int(read_stmt_ip, 1, id)) != SQLITE_OK)
{
*message = sqlite3_errmsg(FTL_db);
logg("networkTable_readIPs(%i): Failed to bind domain (error %d) - %s",
id, rc, sqlite3_errmsg(FTL_db));
id, rc, *message);
sqlite3_reset(read_stmt_ip);
sqlite3_finalize(read_stmt_ip);
return false;
@ -1035,7 +1038,7 @@ bool networkTable_readIPs(const int id)
return true;
}
const char *networkTable_readIPsGetRecord(void)
bool networkTable_readIPsGetRecord(network_addresses_record *network_addresses, const char **message)
{
// Perform step
const int rc = sqlite3_step(read_stmt_ip);
@ -1043,7 +1046,8 @@ const char *networkTable_readIPsGetRecord(void)
// Valid row
if(rc == SQLITE_ROW)
{
return (char*)sqlite3_column_text(read_stmt_ip, 0);
network_addresses->ip = (char*)sqlite3_column_text(read_stmt_ip, 0);
return true;
}
// Check for error. An error happened when the result is neither
@ -1051,13 +1055,14 @@ const char *networkTable_readIPsGetRecord(void)
// SQLITE_DONE (we are finished reading the table)
if(rc != SQLITE_DONE)
{
*message = sqlite3_errmsg(FTL_db);
logg("networkTable_readDevicesGetIP() - SQL error step (%i): %s",
rc, sqlite3_errmsg(FTL_db));
return NULL;
rc, *message);
return false;
}
// Finished reading, nothing to get here
return NULL;
return false;
}
// Finalize statement of a gravity database transaction

View File

@ -17,23 +17,27 @@ void updateMACVendorRecords(void);
bool unify_hwaddr(void);
char* getDatabaseHostname(const char* ipaddr) __attribute__((malloc));
typedef struct networkrecord {
unsigned int id;
typedef struct {
unsigned int id;
const char *hwaddr;
const char* interface;
const char *name;
const char *macVendor;
unsigned long numQueries;
time_t firstSeen;
time_t lastQuery;
} networkrecord;
const char *iface;
const char *name;
const char *macVendor;
unsigned long numQueries;
time_t firstSeen;
time_t lastQuery;
} network_record;
bool networkTable_readDevices(const char **message);
bool networkTable_readDevicesGetRecord(networkrecord *network, const char **message);
bool networkTable_readDevicesGetRecord(network_record *network, const char **message);
void networkTable_readDevicesFinalize(void);
bool networkTable_readIPs(const int id);
const char *networkTable_readIPsGetRecord(void);
typedef struct {
const char *ip;
} network_addresses_record;
bool networkTable_readIPs(const int id, const char **message);
bool networkTable_readIPsGetRecord(network_addresses_record *network_addresses, const char **message);
void networkTable_readIPsFinalize(void);
#endif //NETWORKTABLE_H