Implement proper error handling for network table IP address sub-query
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
parent
ae035c4ba7
commit
822f8c6a28
|
@ -14,6 +14,7 @@ set(sources
|
|||
dns.c
|
||||
dns.h
|
||||
ftl.c
|
||||
network.c
|
||||
routes.c
|
||||
routes.h
|
||||
settings.c
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue