diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 9c8f1f29..5257c708 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -14,6 +14,7 @@ set(sources dns.c dns.h ftl.c + network.c routes.c routes.h settings.c diff --git a/src/api/ftl.c b/src/api/ftl.c index 234cacfd..eae582ec 100644 --- a/src/api/ftl.c +++ b/src/api/ftl.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); -} diff --git a/src/api/network.c b/src/api/network.c new file mode 100644 index 00000000..b57c7e11 --- /dev/null +++ b/src/api/network.c @@ -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); +} diff --git a/src/api/routes.c b/src/api/routes.c index 613e72d1..e276e9a2 100644 --- a/src/api/routes.c +++ b/src/api/routes.c @@ -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)) diff --git a/src/api/routes.h b/src/api/routes.h index e9a73668..2f0862f7 100644 --- a/src/api/routes.h +++ b/src/api/routes.h @@ -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); diff --git a/src/database/network-table.c b/src/database/network-table.c index 567e44cf..715eb9a6 100644 --- a/src/database/network-table.c +++ b/src/database/network-table.c @@ -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 diff --git a/src/database/network-table.h b/src/database/network-table.h index f41e6a89..d0685ae6 100644 --- a/src/database/network-table.h +++ b/src/database/network-table.h @@ -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