Move /api/ftl/gateway -> /api/network/gateway, /api/ftl/interfaces -> /api/network/interfaces, /api/ftl/network -> /api/network/devices, and /api/ftl/config -> /api/config and add documentation for /api/network/devices

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-01-02 20:52:13 +01:00
parent 69aaa71c97
commit 8df3500c83
18 changed files with 945 additions and 846 deletions

View File

@@ -15,8 +15,8 @@ set(sources
dns.c
dns.h
ftl.c
ftl/config.c
ftl/network.c
config.c
network.c
history.c
list.c
queries.c

View File

@@ -35,10 +35,6 @@ static struct {
{ "/api/ftl/logs/dns", api_ftl_logs_dns, { false, false } },
{ "/api/ftl/sysinfo", api_ftl_sysinfo, { false, false } },
{ "/api/ftl/dbinfo", api_ftl_dbinfo, { false, false } },
{ "/api/ftl/config", api_ftl_config, { false, false } },
{ "/api/ftl/gateway", api_ftl_gateway, { false, false } },
{ "/api/ftl/interfaces", api_ftl_interfaces, { false, false } },
{ "/api/ftl/network", api_ftl_network, { false, false } },
{ "/api/ftl/endpoints", api_ftl_endpoints, { false, false } },
{ "/api/history/clients", api_history_clients, { false, false } },
{ "/api/history", api_history, { false, false } },
@@ -63,6 +59,10 @@ static struct {
{ "/api/version", api_version, { false, false } },
{ "/api/auth", api_auth, { false, false } },
{ "/api/settings/web", api_settings_web, { false, false } },
{ "/api/config", api_config, { false, false } },
{ "/api/network/gateway", api_network_gateway, { false, false } },
{ "/api/network/interfaces", api_network_interfaces, { false, false } },
{ "/api/network/devices", api_network_devices, { false, false } },
{ "/api/docs", api_docs, { false, false } },
};
@@ -130,9 +130,7 @@ static int api_ftl_endpoints(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *json = JSON_NEW_OBJECT();
cJSON *endpoints = JSON_NEW_ARRAY();

View File

@@ -50,12 +50,12 @@ int api_ftl_dbinfo(struct ftl_conn *api);
int api_ftl_sysinfo(struct ftl_conn *api);
int get_ftl_obj(struct ftl_conn *api, cJSON *ftl, const bool is_locked);
int get_system_obj(struct ftl_conn *api, cJSON *system);
int api_ftl_config(struct ftl_conn *api);
int api_ftl_gateway(struct ftl_conn *api);
int api_ftl_interfaces(struct ftl_conn *api);
int api_ftl_network(struct ftl_conn *api);
int api_config(struct ftl_conn *api);
// Network methods
int api_network_gateway(struct ftl_conn *api);
int api_network_interfaces(struct ftl_conn *api);
int api_network_devices(struct ftl_conn *api);
// DNS methods
int api_dns_blocking(struct ftl_conn *api);

View File

@@ -37,13 +37,11 @@
// Interate through directories
#include <dirent.h>
int api_ftl_config(struct ftl_conn *api)
int api_config(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *config_j = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(config_j, "debug",config.debug); /* TODO: Split into individual fields */

View File

@@ -51,9 +51,7 @@ static int set_blocking(struct ftl_conn *api)
{
// Verify requesting client is allowed to access this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
if (api->payload.json == NULL) {
return send_json_error(api, 400,
@@ -125,9 +123,7 @@ int api_dns_cache(struct ftl_conn *api)
{
// Verify requesting client is allowed to access this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
struct cache_info ci = { 0 };
get_dnsmasq_cache_info(&ci);

View File

@@ -0,0 +1,238 @@
openapi: 3.0.2
components:
paths:
config:
get:
summary: Get info about the config of your Pi-hole
tags:
- "FTL information"
operationId: "get_config"
description: |
This API hook returns infos about the config of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'config.yaml#/components/schemas/config'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
schemas:
config:
type: object
properties:
config:
type: object
properties:
debug:
type: integer
example: 0
dns:
type: object
properties:
CNAMEdeepInspect:
type: boolean
example: true
blockESNI:
type: boolean
example: true
EDN0ECS:
type: boolean
example: true
ignoreLocalhost:
type: boolean
example: false
showDNSSEC:
type: boolean
example: true
analyzeAAAA:
type: boolean
example: true
analyzeOnlyAandAAAA:
type: boolean
example: false
piholePTR:
type: string
example: "PI.HOLE"
replyWhenBusy:
type: string
example: "ALLOW"
blockTTL:
type: integer
example: 2
blockingmode:
type: string
example: "NULL"
port:
type: integer
example: 53
specialDomains:
type: object
properties:
mozillaCanary:
type: boolean
example: true
iCloudPrivateRelay:
type: boolean
example: true
reply:
type: object
properties:
host:
type: object
properties:
ipv4:
type: string
example: ""
ipv6:
type: string
example: ""
blocking:
type: object
properties:
ipv4:
type: string
example: ""
ipv6:
type: string
example: ""
rateLimit:
type: object
properties:
count:
type: integer
example: 1000
interval:
type: integer
example: 60
resolver:
type: object
properties:
resolveIPv4:
type: boolean
example: true
resolveIPv6:
type: boolean
example: true
networkNames:
type: boolean
example: true
refreshNames:
type: string
example: "IPV4_ONLY"
database:
type: object
properties:
DBimport:
type: boolean
example: true
DBexport:
type: boolean
example: true
maxHistory:
type: integer
example: 86400
maxDBdays:
type: integer
example: 365
DBinterval:
type: integer
example: 60
network:
type: object
properties:
parseARPcache:
type: boolean
example: true
expire:
type: integer
example: 365
http:
type: object
properties:
localAPIauth:
type: boolean
example: true
prettyJSON:
type: boolean
example: false
sessionTimeout:
type: integer
example: 300
domain:
type: string
example: "pi.hole"
acl:
type: string
example: "+0.0.0.0/0"
port:
type: string
example: "8080,[::]:8080"
paths:
type: object
properties:
webroot:
type: string
example: "/var/www/html"
webhome:
type: string
example: "/admin/"
files:
type: object
properties:
log:
type: string
example: "/var/log/pihole/FTL.log"
pid:
type: string
example: "/run/pihole-FTL.pid"
database:
type: string
example: "/etc/pihole/pihole-FTL.db"
gravity:
type: string
example: "/etc/pihole/gravity.db"
macvendor:
type: string
example: "/etc/pihole/macvendor.db"
setupVars:
type: string
example: "/etc/pihole/setupVars.conf"
http_info:
type: string
example: "/var/log/pihole/HTTP_info.log"
ph7_error:
type: string
example: "/var/log/pihole/PH7_error.log"
misc:
type: object
properties:
nice:
type: integer
example: -10
delay_startup:
type: integer
example: 0
addr2line:
type: boolean
example: true
privacylevel:
type: integer
example: 0
check:
type: object
properties:
load:
type: boolean
example: true
shmem:
type: integer
example: 90
disk:
type: integer
example: 90

View File

@@ -113,69 +113,6 @@ components:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
gateway:
get:
summary: Get info about the gateway of your Pi-hole
tags:
- "FTL information"
operationId: "get_gateway"
description: |
This API hook returns infos about the gateway of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'ftl.yaml#/components/schemas/gateway'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
interfaces:
get:
summary: Get info about the interfaces of your Pi-hole
tags:
- "FTL information"
operationId: "get_interfaces"
description: |
This API hook returns infos about the networking interfaces of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'ftl.yaml#/components/schemas/interfaces'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
config:
get:
summary: Get info about the config of your Pi-hole
tags:
- "FTL information"
operationId: "get_config"
description: |
This API hook returns infos about the config of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'ftl.yaml#/components/schemas/config'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
schemas:
client:
remote_addr:
@@ -482,316 +419,6 @@ components:
items:
type: string
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
gateway:
type: object
properties:
address:
type: string
description: Address of the gateway
example: "192.168.0.1"
interface:
type: string
description: Interface of your Pi-hole connected to the gateway
example: "eth0"
interfaces:
type: object
properties:
interfaces:
type: array
description: Interface information
items:
type: object
properties:
name:
type: string
nullable: true
description: Interface name
carrier:
type: boolean
description: If the interface is connected
speed:
type: integer
description: Speed of the interface in Mbit/s (-1 if not applicable)
tx:
type: object
properties:
num:
type: number
description: Number of transmitted data since boot
unit:
type: string
description: Unit of transmitted data since boot
rx:
type: object
properties:
num:
type: number
description: Number of received data since boot
unit:
type: string
description: Unit of received data since boot
ipv4:
type: array
nullable: true
description: Array of associated IPv4 addresses
items:
type: string
ipv6:
type: array
nullable: true
description: Array of associated IPv6 addresses
items:
type: string
example:
- name: "eth0"
default: true
carrier: true
speed: 1000
tx:
num: 10.4
unit: "MB"
rx:
num: 8.1
unit: "MB"
ipv4: ["192.168.0.123"]
ipv6: ["fe80::1234:5678:9abc:def0", "2001:db8::1234:5678:9abc:def0"]
- name: "wlan0"
default: false
carrier: false
speed: -1
tx:
num: 0
unit: "B"
rx:
num: 0
unit: "B"
ipv4: []
ipv6: []
- name: "wg0"
default: false
carrier: true
speed: -1
tx:
num: 170.3
unit: "kB"
rx:
num: 222.3
unit: "kB"
ipv4: ["10.1.0.1"]
ipv6: ["fd00:4711::1"]
config:
type: object
properties:
config:
type: object
properties:
debug:
type: integer
example: 0
dns:
type: object
properties:
CNAMEdeepInspect:
type: boolean
example: true
blockESNI:
type: boolean
example: true
EDN0ECS:
type: boolean
example: true
ignoreLocalhost:
type: boolean
example: false
showDNSSEC:
type: boolean
example: true
analyzeAAAA:
type: boolean
example: true
analyzeOnlyAandAAAA:
type: boolean
example: false
piholePTR:
type: string
example: "PI.HOLE"
replyWhenBusy:
type: string
example: "ALLOW"
blockTTL:
type: integer
example: 2
blockingmode:
type: string
example: "NULL"
port:
type: integer
example: 53
specialDomains:
type: object
properties:
mozillaCanary:
type: boolean
example: true
iCloudPrivateRelay:
type: boolean
example: true
reply:
type: object
properties:
host:
type: object
properties:
ipv4:
type: string
example: ""
ipv6:
type: string
example: ""
blocking:
type: object
properties:
ipv4:
type: string
example: ""
ipv6:
type: string
example: ""
rateLimit:
type: object
properties:
count:
type: integer
example: 1000
interval:
type: integer
example: 60
resolver:
type: object
properties:
resolveIPv4:
type: boolean
example: true
resolveIPv6:
type: boolean
example: true
networkNames:
type: boolean
example: true
refreshNames:
type: string
example: "IPV4_ONLY"
database:
type: object
properties:
DBimport:
type: boolean
example: true
DBexport:
type: boolean
example: true
maxHistory:
type: integer
example: 86400
maxDBdays:
type: integer
example: 365
DBinterval:
type: integer
example: 60
network:
type: object
properties:
parseARPcache:
type: boolean
example: true
expire:
type: integer
example: 365
http:
type: object
properties:
localAPIauth:
type: boolean
example: true
prettyJSON:
type: boolean
example: false
sessionTimeout:
type: integer
example: 300
domain:
type: string
example: "pi.hole"
acl:
type: string
example: "+0.0.0.0/0"
port:
type: string
example: "8080,[::]:8080"
paths:
type: object
properties:
webroot:
type: string
example: "/var/www/html"
webhome:
type: string
example: "/admin/"
files:
type: object
properties:
log:
type: string
example: "/var/log/pihole/FTL.log"
pid:
type: string
example: "/run/pihole-FTL.pid"
database:
type: string
example: "/etc/pihole/pihole-FTL.db"
gravity:
type: string
example: "/etc/pihole/gravity.db"
macvendor:
type: string
example: "/etc/pihole/macvendor.db"
setupVars:
type: string
example: "/etc/pihole/setupVars.conf"
http_info:
type: string
example: "/var/log/pihole/HTTP_info.log"
ph7_error:
type: string
example: "/var/log/pihole/PH7_error.log"
misc:
type: object
properties:
nice:
type: integer
example: -10
delay_startup:
type: integer
example: 0
addr2line:
type: boolean
example: true
privacylevel:
type: integer
example: 0
check:
type: object
properties:
load:
type: boolean
example: true
shmem:
type: integer
example: 90
disk:
type: integer
example: 90
parameters:
logs:

View File

@@ -15,7 +15,6 @@ info:
Most (but not all) endpoints require authentication.
API endpoints requiring authentication will fail with code `401 Unauthorized` when used outside a valid session.
servers:
- url: http://pi.hole:8080/api
- url: 'http://{url}:{port}/{path}'
variables:
url:
@@ -45,6 +44,8 @@ tags:
description: Methods used to manage lists on your Pi-hole
- name: "FTL information"
description: Methods used to gather advanced data from your Pi-hole
- name: "Network information"
description: Methods used to gather advanced information about your network
paths:
/auth:
@@ -110,14 +111,17 @@ paths:
/ftl/endpoints:
$ref: 'ftl.yaml#/components/paths/endpoints'
/ftl/gateway:
$ref: 'ftl.yaml#/components/paths/gateway'
/config:
$ref: 'config.yaml#/components/paths/config'
/ftl/interfaces:
$ref: 'ftl.yaml#/components/paths/interfaces'
/network/devices:
$ref: 'network.yaml#/components/paths/devices'
/ftl/config:
$ref: 'ftl.yaml#/components/paths/config'
/network/gateway:
$ref: 'network.yaml#/components/paths/gateway'
/network/interfaces:
$ref: 'network.yaml#/components/paths/interfaces'
/version:
$ref: 'version.yaml#/components/paths/version'

View File

@@ -0,0 +1,244 @@
openapi: 3.0.2
components:
paths:
gateway:
get:
summary: Get info about the gateway of your Pi-hole
tags:
- "Network information"
operationId: "get_gateway"
description: |
This API hook returns infos about the gateway of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'network.yaml#/components/schemas/gateway'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
interfaces:
get:
summary: Get info about the interfaces of your Pi-hole
tags:
- "Network information"
operationId: "get_interfaces"
description: |
This API hook returns infos about the networking interfaces of your Pi-hole.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'network.yaml#/components/schemas/interfaces'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
devices:
get:
summary: Get info about the devices in your local network as seen by your Pi-hole
tags:
- "Network information"
operationId: "get_network"
description: |
This API hook returns infos about the devices in your local network as seen by your Pi-hole. By default, this number of shown devices is limited to 10. Devices are ordered by when your Pi-hole has received the last query from this device (most recent first)
parameters:
- $ref: 'network.yaml#/components/parameters/devices/max_devices'
- $ref: 'network.yaml#/components/parameters/devices/max_addresses'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'network.yaml#/components/schemas/devices'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: 'common.yaml#/components/errors/unauthorized'
schemas:
gateway:
type: object
properties:
address:
type: string
description: Address of the gateway
example: "192.168.0.1"
interface:
type: string
description: Interface of your Pi-hole connected to the gateway
example: "eth0"
interfaces:
type: object
properties:
interfaces:
type: array
description: Interface information
items:
type: object
properties:
name:
type: string
nullable: true
description: Interface name
carrier:
type: boolean
description: If the interface is connected
speed:
type: integer
description: Speed of the interface in Mbit/s (-1 if not applicable)
tx:
type: object
properties:
num:
type: number
description: Number of transmitted data since boot
unit:
type: string
description: Unit of transmitted data since boot
rx:
type: object
properties:
num:
type: number
description: Number of received data since boot
unit:
type: string
description: Unit of received data since boot
ipv4:
type: array
nullable: true
description: Array of associated IPv4 addresses
items:
type: string
ipv6:
type: array
nullable: true
description: Array of associated IPv6 addresses
items:
type: string
example:
- name: "eth0"
default: true
carrier: true
speed: 1000
tx:
num: 10.4
unit: "MB"
rx:
num: 8.1
unit: "MB"
ipv4: ["192.168.0.123"]
ipv6: ["fe80::1234:5678:9abc:def0", "2001:db8::1234:5678:9abc:def0"]
- name: "wlan0"
default: false
carrier: false
speed: -1
tx:
num: 0
unit: "B"
rx:
num: 0
unit: "B"
ipv4: []
ipv6: []
- name: "wg0"
default: false
carrier: true
speed: -1
tx:
num: 170.3
unit: "kB"
rx:
num: 222.3
unit: "kB"
ipv4: ["10.1.0.1"]
ipv6: ["fd00:4711::1"]
devices:
type: object
properties:
devices:
type: array
description: Array of devices
items:
type: object
properties:
id:
type: integer
description: Device network table ID
example: 1
hwaddr:
type: string
description: MAC address of this device
example: 00:11:22:33:44:55
interface:
type: string
description: Interface this device is connected to
example: enp2s0
firstSeen:
type: interger
description: Unix timestamp when this device was first seen by your Pi-hole
example: 1664623620
lastQuery:
type: interger
description: Unix timestamp when your Pi-hole received the last query from this device
example: 1664688620
numQueries:
type: integer
description: Total number of queries your Pi-hole has received from this device
example: 585462
macVendor:
type: string
description: Vendor name associated with the device's MAC address
example: "Digital Data Communications Asia Co.,Ltd"
ips:
type: array
items:
type: object
properties:
ip:
type: string
description: Associated IP address (can be IPv4 or IPv6)
example: "192.168.1.51"
name:
type: string
description: Associated hostname (can be null)
example: ubuntu-server
lastSeen:
description: Unix timestamp when your Pi-hole has seen this address the last time
example: 1664688620
nameUpdated:
description: Unix timestamp when device updated its hostname the last time
example: 1664688620
parameters:
devices:
max_devices:
in: query
description: (Optional) Maximum number of devices to show
name: max_devices
schema:
type: integer
required: false
example: 10
max_addresses:
in: query
description: (Optional) Maximum number of addresses to show per device
name: max_addresses
schema:
type: integer
required: false
example: 3

View File

@@ -32,10 +32,6 @@
#include "../config/config.h"
// struct clientsData
#include "../datastructure.h"
// Routing information and flags
#include <net/route.h>
// Interate through directories
#include <dirent.h>
int api_ftl_client(struct ftl_conn *api)
{
@@ -70,9 +66,7 @@ int api_ftl_logs_dns(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
unsigned int start = 0u;
if(api->request->query_string != NULL)
@@ -134,9 +128,7 @@ int api_ftl_dbinfo(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *json = JSON_NEW_OBJECT();
@@ -417,9 +409,7 @@ int get_ftl_obj(struct ftl_conn *api, cJSON *ftl, const bool is_locked)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *database = JSON_NEW_OBJECT();
@@ -475,9 +465,7 @@ int api_ftl_sysinfo(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *json = JSON_NEW_OBJECT();
@@ -498,300 +486,3 @@ int api_ftl_sysinfo(struct ftl_conn *api)
JSON_ADD_ITEM_TO_OBJECT(json, "ftl", ftl);
JSON_SEND_OBJECT(json);
}
static bool getDefaultInterface(char iface[IF_NAMESIZE], in_addr_t *gw)
{
// Get IPv4 default route gateway and associated interface
long dest_r = 0, gw_r = 0;
int flags = 0, metric = 0, minmetric = __INT_MAX__;
char iface_r[IF_NAMESIZE] = { 0 };
char buf[1024] = { 0 };
FILE *file;
if((file = fopen("/proc/net/route", "r")))
{
// Parse /proc/net/route - the kernel's IPv4 routing table
while(fgets(buf, sizeof(buf), file))
{
if(sscanf(buf, "%s %lx %lx %x %*i %*i %i", iface_r, &dest_r, &gw_r, &flags, &metric) != 5)
continue;
// Only anaylze routes which are UP and whose
// destinations are a gateway
if(!(flags & RTF_UP) || !(flags & RTF_GATEWAY))
continue;
// Only analyze "catch all" routes (destination 0.0.0.0)
if(dest_r != 0)
continue;
// Store default gateway, overwrite if we find a route with
// a lower metric
if(metric < minmetric)
{
minmetric = metric;
*gw = gw_r;
strcpy(iface, iface_r);
log_debug(DEBUG_API, "Reading interfaces: flags: %i, addr: %s, iface: %s, metric: %i, minmetric: %i",
flags, inet_ntoa(*(struct in_addr *) gw), iface, metric, minmetric);
}
}
fclose(file);
}
else
log_err("Cannot read /proc/net/route: %s", strerror(errno));
// Return success based on having found the default gateway's address
return gw != 0;
}
int api_ftl_gateway(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
in_addr_t gw = 0;
char iface[IF_NAMESIZE] = { 0 };
// Get default interface
getDefaultInterface(iface, &gw);
// Generate JSON response
cJSON *json = JSON_NEW_OBJECT();
const char *gwaddr = inet_ntoa(*(struct in_addr *) &gw);
JSON_COPY_STR_TO_OBJECT(json, "address", gwaddr);
JSON_REF_STR_IN_OBJECT(json, "interface", iface);
JSON_SEND_OBJECT(json);
}
int api_ftl_interfaces(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
cJSON *json = JSON_NEW_OBJECT();
// Get interface with default route
in_addr_t gw = 0;
char default_iface[IF_NAMESIZE] = { 0 };
getDefaultInterface(default_iface, &gw);
// Enumerate and list interfaces
// Loop over interfaces and extract information
DIR *dfd;
FILE *f;
struct dirent *dp;
size_t tx_sum = 0, rx_sum = 0;
char fname[64 + IF_NAMESIZE] = { 0 };
char readbuffer[1024] = { 0 };
// Open /sys/class/net directory
if ((dfd = opendir("/sys/class/net")) == NULL)
{
log_err("API: Cannot access /sys/class/net");
return 500;
}
// Get IP addresses of all interfaces on this machine
struct ifaddrs *ifap = NULL;
if(getifaddrs(&ifap) == -1)
log_err("API: Cannot get interface addresses: %s", strerror(errno));
cJSON *interfaces = JSON_NEW_ARRAY();
// Walk /sys/class/net directory
while ((dp = readdir(dfd)) != NULL)
{
// Skip "." and ".."
if(!dp->d_name || strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue;
// Create new interface record
cJSON *iface = JSON_NEW_OBJECT();
// Extract interface name
const char *iface_name = dp->d_name;
JSON_COPY_STR_TO_OBJECT(iface, "name", iface_name);
// Is this the default interface?
const bool is_default_iface = strcmp(iface_name, default_iface) == 0;
JSON_ADD_BOOL_TO_OBJECT(iface, "default", is_default_iface);
// Extract carrier status
bool carrier = false;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/carrier", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fgets(readbuffer, sizeof(readbuffer)-1, f) != NULL)
carrier = readbuffer[0] == '1';
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
JSON_ADD_BOOL_TO_OBJECT(iface, "carrier", carrier);
// Extract link speed (may not be possible, e.g., for WiFi devices with dynamic link speeds)
int speed = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/speed", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%i", &(speed)) != 1)
speed = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
JSON_ADD_NUMBER_TO_OBJECT(iface, "speed", speed);
// Get total transmitted bytes
ssize_t tx_bytes = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/statistics/tx_bytes", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%zi", &(tx_bytes)) != 1)
tx_bytes = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
// Format transmitted bytes
double tx = 0.0;
char tx_unit[3] = { 0 };
format_memory_size(tx_unit, tx_bytes, &tx);
if(tx_unit[0] != '\0')
tx_unit[1] = 'B';
// Add transmitted bytes to interface record
cJSON *tx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(tx_json, "num", tx);
JSON_COPY_STR_TO_OBJECT(tx_json, "unit", tx_unit);
JSON_ADD_ITEM_TO_OBJECT(iface, "tx", tx_json);
// Get total received bytes
ssize_t rx_bytes = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/statistics/rx_bytes", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%zi", &(rx_bytes)) != 1)
rx_bytes = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
// Format received bytes
double rx = 0.0;
char rx_unit[3] = { 0 };
format_memory_size(rx_unit, rx_bytes, &rx);
if(rx_unit[0] != '\0')
rx_unit[1] = 'B';
// Add received bytes to JSON object
cJSON *rx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(rx_json, "num", rx);
JSON_COPY_STR_TO_OBJECT(rx_json, "unit", rx_unit);
JSON_ADD_ITEM_TO_OBJECT(iface, "rx", rx_json);
// Get IP address(es) of this interface
if(ifap)
{
// Walk through linked list of interface addresses
cJSON *ipv4 = JSON_NEW_ARRAY();
cJSON *ipv6 = JSON_NEW_ARRAY();
for(struct ifaddrs *ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
{
// Skip interfaces without an address and those
// not matching the current interface
if(ifa->ifa_addr == NULL || strcmp(ifa->ifa_name, iface_name) != 0)
continue;
// If we reach this point, we found the correct interface
const sa_family_t family = ifa->ifa_addr->sa_family;
char host[NI_MAXHOST] = { 0 };
if(family == AF_INET || family == AF_INET6)
{
// Get IP address
const int s = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (s != 0)
{
log_warn("API: getnameinfo() failed: %s\n", gai_strerror(s));
continue;
}
if(family == AF_INET)
{
JSON_COPY_STR_TO_ARRAY(ipv4, host);
}
else if(family == AF_INET6)
{
JSON_COPY_STR_TO_ARRAY(ipv6, host);
}
}
}
JSON_ADD_ITEM_TO_OBJECT(iface, "ipv4", ipv4);
JSON_ADD_ITEM_TO_OBJECT(iface, "ipv6", ipv6);
}
// Sum up transmitted and received bytes
tx_sum += tx_bytes;
rx_sum += rx_bytes;
// Add interface to array
JSON_ADD_ITEM_TO_ARRAY(interfaces, iface);
}
freeifaddrs(ifap);
cJSON *sum = JSON_NEW_OBJECT();
JSON_COPY_STR_TO_OBJECT(sum, "name", "sum");
JSON_ADD_BOOL_TO_OBJECT(sum, "carrier", true);
JSON_ADD_NUMBER_TO_OBJECT(sum, "speed", 0);
// Format transmitted bytes
double tx = 0.0;
char tx_unit[3] = { 0 };
format_memory_size(tx_unit, tx_sum, &tx);
if(tx_unit[0] != '\0')
tx_unit[1] = 'B';
// Add transmitted bytes to interface record
cJSON *tx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(tx_json, "num", tx);
JSON_COPY_STR_TO_OBJECT(tx_json, "unit", tx_unit);
JSON_ADD_ITEM_TO_OBJECT(sum, "tx", tx_json);
// Format received bytes
double rx = 0.0;
char rx_unit[3] = { 0 };
format_memory_size(rx_unit, rx_sum, &rx);
if(rx_unit[0] != '\0')
rx_unit[1] = 'B';
// Add received bytes to JSON object
cJSON *rx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(rx_json, "num", rx);
JSON_COPY_STR_TO_OBJECT(rx_json, "unit", rx_unit);
JSON_ADD_ITEM_TO_OBJECT(sum, "rx", rx_json);
cJSON *ipv4 = JSON_NEW_ARRAY();
cJSON *ipv6 = JSON_NEW_ARRAY();
JSON_ADD_ITEM_TO_OBJECT(sum, "ipv4", ipv4);
JSON_ADD_ITEM_TO_OBJECT(sum, "ipv6", ipv6);
// Add interface to array
JSON_ADD_ITEM_TO_ARRAY(interfaces, sum);
JSON_ADD_ITEM_TO_OBJECT(json, "interfaces", interfaces);
JSON_SEND_OBJECT(json);
}

View File

@@ -1,115 +0,0 @@
/* 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 "api/api.h"
// networkrecord
#include "database/network-table.h"
// dbopen()
#include "database/common.h"
int api_ftl_network(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Open pihole-FTL.db database file
sqlite3_stmt *device_stmt = NULL, *ip_stmt = NULL;
sqlite3 *db = dbopen(false);
if(db == NULL)
{
log_warn("Failed to open database in networkTable_readDevices()");
return false;
}
const char *sql_msg = NULL;
if(!networkTable_readDevices(db, &device_stmt, &sql_msg))
{
// Add SQL message (may be NULL = not available)
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table",
sql_msg);
}
// Read record for a single device
cJSON *json = JSON_NEW_ARRAY();
network_record network;
while(networkTable_readDevicesGetRecord(device_stmt, &network, &sql_msg))
{
cJSON *item = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(item, "id", network.id);
JSON_COPY_STR_TO_OBJECT(item, "hwaddr", network.hwaddr);
JSON_COPY_STR_TO_OBJECT(item, "interface", network.iface);
JSON_ADD_NUMBER_TO_OBJECT(item, "firstSeen", network.firstSeen);
JSON_ADD_NUMBER_TO_OBJECT(item, "lastQuery", network.lastQuery);
JSON_ADD_NUMBER_TO_OBJECT(item, "numQueries", network.numQueries);
JSON_COPY_STR_TO_OBJECT(item, "macVendor", network.macVendor);
// Build array of all IP addresses known associated to this client
cJSON *ips = JSON_NEW_ARRAY();
if(networkTable_readIPs(db, &ip_stmt, network.id, &sql_msg))
{
// Walk known IP addresses + names
network_addresses_record network_address;
while(networkTable_readIPsGetRecord(ip_stmt, &network_address, &sql_msg))
{
cJSON *ip = JSON_NEW_OBJECT();
JSON_COPY_STR_TO_OBJECT(ip, "ip", network_address.ip);
JSON_COPY_STR_TO_OBJECT(ip, "name", network_address.name);
JSON_ADD_NUMBER_TO_OBJECT(ip, "lastSeen", network_address.lastSeen);
JSON_ADD_NUMBER_TO_OBJECT(ip, "nameUpdated", network_address.nameUpdated);
JSON_ADD_ITEM_TO_ARRAY(ips, ip);
}
// Possible error handling
if(sql_msg != NULL)
{
cJSON_Delete(ips);
cJSON_Delete(json);
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (getting IP records)",
sql_msg);
}
// Finalize sub-query
networkTable_readIPsFinalize(ip_stmt);
}
// Add array of IP addresses to device
JSON_ADD_ITEM_TO_OBJECT(item, "ips", ips);
// Add device to array of all devices
JSON_ADD_ITEM_TO_ARRAY(json, item);
}
if(sql_msg != NULL)
{
cJSON_Delete(json);
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (step)",
sql_msg);
}
// Finalize query
networkTable_readDevicesFinalize(device_stmt);
dbclose(&db);
// Return data to user
JSON_SEND_OBJECT(json);
}

View File

@@ -88,9 +88,7 @@ int api_history_clients(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Find minimum ID to send
for(int slot = 0; slot < OVERTIME_SLOTS; slot++)

View File

@@ -353,9 +353,7 @@ int api_list(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
char payload[MAX_PAYLOAD_BYTES] = { 0 };
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
enum gravity_list_type listtype;
bool can_modify = false;

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

@@ -0,0 +1,426 @@
/* 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 "api/api.h"
// Routing information and flags
#include <net/route.h>
// Interate through directories
#include <dirent.h>
// networkrecord
#include "database/network-table.h"
// dbopen()
#include "database/common.h"
static bool getDefaultInterface(char iface[IF_NAMESIZE], in_addr_t *gw)
{
// Get IPv4 default route gateway and associated interface
long dest_r = 0, gw_r = 0;
int flags = 0, metric = 0, minmetric = __INT_MAX__;
char iface_r[IF_NAMESIZE] = { 0 };
char buf[1024] = { 0 };
FILE *file;
if((file = fopen("/proc/net/route", "r")))
{
// Parse /proc/net/route - the kernel's IPv4 routing table
while(fgets(buf, sizeof(buf), file))
{
if(sscanf(buf, "%s %lx %lx %x %*i %*i %i", iface_r, &dest_r, &gw_r, &flags, &metric) != 5)
continue;
// Only anaylze routes which are UP and whose
// destinations are a gateway
if(!(flags & RTF_UP) || !(flags & RTF_GATEWAY))
continue;
// Only analyze "catch all" routes (destination 0.0.0.0)
if(dest_r != 0)
continue;
// Store default gateway, overwrite if we find a route with
// a lower metric
if(metric < minmetric)
{
minmetric = metric;
*gw = gw_r;
strcpy(iface, iface_r);
log_debug(DEBUG_API, "Reading interfaces: flags: %i, addr: %s, iface: %s, metric: %i, minmetric: %i",
flags, inet_ntoa(*(struct in_addr *) gw), iface, metric, minmetric);
}
}
fclose(file);
}
else
log_err("Cannot read /proc/net/route: %s", strerror(errno));
// Return success based on having found the default gateway's address
return gw != 0;
}
int api_network_gateway(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
return send_json_unauthorized(api);
in_addr_t gw = 0;
char iface[IF_NAMESIZE] = { 0 };
// Get default interface
getDefaultInterface(iface, &gw);
// Generate JSON response
cJSON *json = JSON_NEW_OBJECT();
const char *gwaddr = inet_ntoa(*(struct in_addr *) &gw);
JSON_COPY_STR_TO_OBJECT(json, "address", gwaddr);
JSON_REF_STR_IN_OBJECT(json, "interface", iface);
JSON_SEND_OBJECT(json);
}
int api_network_interfaces(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
return send_json_unauthorized(api);
cJSON *json = JSON_NEW_OBJECT();
// Get interface with default route
in_addr_t gw = 0;
char default_iface[IF_NAMESIZE] = { 0 };
getDefaultInterface(default_iface, &gw);
// Enumerate and list interfaces
// Loop over interfaces and extract information
DIR *dfd;
FILE *f;
struct dirent *dp;
size_t tx_sum = 0, rx_sum = 0;
char fname[64 + IF_NAMESIZE] = { 0 };
char readbuffer[1024] = { 0 };
// Open /sys/class/net directory
if ((dfd = opendir("/sys/class/net")) == NULL)
{
log_err("API: Cannot access /sys/class/net");
return 500;
}
// Get IP addresses of all interfaces on this machine
struct ifaddrs *ifap = NULL;
if(getifaddrs(&ifap) == -1)
log_err("API: Cannot get interface addresses: %s", strerror(errno));
cJSON *interfaces = JSON_NEW_ARRAY();
// Walk /sys/class/net directory
while ((dp = readdir(dfd)) != NULL)
{
// Skip "." and ".."
if(!dp->d_name || strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue;
// Create new interface record
cJSON *iface = JSON_NEW_OBJECT();
// Extract interface name
const char *iface_name = dp->d_name;
JSON_COPY_STR_TO_OBJECT(iface, "name", iface_name);
// Is this the default interface?
const bool is_default_iface = strcmp(iface_name, default_iface) == 0;
JSON_ADD_BOOL_TO_OBJECT(iface, "default", is_default_iface);
// Extract carrier status
bool carrier = false;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/carrier", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fgets(readbuffer, sizeof(readbuffer)-1, f) != NULL)
carrier = readbuffer[0] == '1';
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
JSON_ADD_BOOL_TO_OBJECT(iface, "carrier", carrier);
// Extract link speed (may not be possible, e.g., for WiFi devices with dynamic link speeds)
int speed = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/speed", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%i", &(speed)) != 1)
speed = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
JSON_ADD_NUMBER_TO_OBJECT(iface, "speed", speed);
// Get total transmitted bytes
ssize_t tx_bytes = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/statistics/tx_bytes", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%zi", &(tx_bytes)) != 1)
tx_bytes = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
// Format transmitted bytes
double tx = 0.0;
char tx_unit[3] = { 0 };
format_memory_size(tx_unit, tx_bytes, &tx);
if(tx_unit[0] != '\0')
tx_unit[1] = 'B';
// Add transmitted bytes to interface record
cJSON *tx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(tx_json, "num", tx);
JSON_COPY_STR_TO_OBJECT(tx_json, "unit", tx_unit);
JSON_ADD_ITEM_TO_OBJECT(iface, "tx", tx_json);
// Get total received bytes
ssize_t rx_bytes = -1;
snprintf(fname, sizeof(fname)-1, "/sys/class/net/%s/statistics/rx_bytes", iface_name);
if((f = fopen(fname, "r")) != NULL)
{
if(fscanf(f, "%zi", &(rx_bytes)) != 1)
rx_bytes = -1;
fclose(f);
}
else
log_err("Cannot read %s: %s", fname, strerror(errno));
// Format received bytes
double rx = 0.0;
char rx_unit[3] = { 0 };
format_memory_size(rx_unit, rx_bytes, &rx);
if(rx_unit[0] != '\0')
rx_unit[1] = 'B';
// Add received bytes to JSON object
cJSON *rx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(rx_json, "num", rx);
JSON_COPY_STR_TO_OBJECT(rx_json, "unit", rx_unit);
JSON_ADD_ITEM_TO_OBJECT(iface, "rx", rx_json);
// Get IP address(es) of this interface
if(ifap)
{
// Walk through linked list of interface addresses
cJSON *ipv4 = JSON_NEW_ARRAY();
cJSON *ipv6 = JSON_NEW_ARRAY();
for(struct ifaddrs *ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
{
// Skip interfaces without an address and those
// not matching the current interface
if(ifa->ifa_addr == NULL || strcmp(ifa->ifa_name, iface_name) != 0)
continue;
// If we reach this point, we found the correct interface
const sa_family_t family = ifa->ifa_addr->sa_family;
char host[NI_MAXHOST] = { 0 };
if(family == AF_INET || family == AF_INET6)
{
// Get IP address
const int s = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (s != 0)
{
log_warn("API: getnameinfo() failed: %s\n", gai_strerror(s));
continue;
}
if(family == AF_INET)
{
JSON_COPY_STR_TO_ARRAY(ipv4, host);
}
else if(family == AF_INET6)
{
JSON_COPY_STR_TO_ARRAY(ipv6, host);
}
}
}
JSON_ADD_ITEM_TO_OBJECT(iface, "ipv4", ipv4);
JSON_ADD_ITEM_TO_OBJECT(iface, "ipv6", ipv6);
}
// Sum up transmitted and received bytes
tx_sum += tx_bytes;
rx_sum += rx_bytes;
// Add interface to array
JSON_ADD_ITEM_TO_ARRAY(interfaces, iface);
}
freeifaddrs(ifap);
cJSON *sum = JSON_NEW_OBJECT();
JSON_COPY_STR_TO_OBJECT(sum, "name", "sum");
JSON_ADD_BOOL_TO_OBJECT(sum, "carrier", true);
JSON_ADD_NUMBER_TO_OBJECT(sum, "speed", 0);
// Format transmitted bytes
double tx = 0.0;
char tx_unit[3] = { 0 };
format_memory_size(tx_unit, tx_sum, &tx);
if(tx_unit[0] != '\0')
tx_unit[1] = 'B';
// Add transmitted bytes to interface record
cJSON *tx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(tx_json, "num", tx);
JSON_COPY_STR_TO_OBJECT(tx_json, "unit", tx_unit);
JSON_ADD_ITEM_TO_OBJECT(sum, "tx", tx_json);
// Format received bytes
double rx = 0.0;
char rx_unit[3] = { 0 };
format_memory_size(rx_unit, rx_sum, &rx);
if(rx_unit[0] != '\0')
rx_unit[1] = 'B';
// Add received bytes to JSON object
cJSON *rx_json = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(rx_json, "num", rx);
JSON_COPY_STR_TO_OBJECT(rx_json, "unit", rx_unit);
JSON_ADD_ITEM_TO_OBJECT(sum, "rx", rx_json);
cJSON *ipv4 = JSON_NEW_ARRAY();
cJSON *ipv6 = JSON_NEW_ARRAY();
JSON_ADD_ITEM_TO_OBJECT(sum, "ipv4", ipv4);
JSON_ADD_ITEM_TO_OBJECT(sum, "ipv6", ipv6);
// Add interface to array
JSON_ADD_ITEM_TO_ARRAY(interfaces, sum);
JSON_ADD_ITEM_TO_OBJECT(json, "interfaces", interfaces);
JSON_SEND_OBJECT(json);
}
int api_network_devices(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
return send_json_unauthorized(api);
// Does the user request a custom number of devices to be included?
unsigned int device_count = 10;
get_uint_var(api->request->query_string, "device_count", &device_count);
// Does the user request a custom number of addresses per device to be included?
unsigned int address_count = 3;
get_uint_var(api->request->query_string, "address_count", &address_count);
// Open pihole-FTL.db database file
sqlite3_stmt *device_stmt = NULL, *ip_stmt = NULL;
sqlite3 *db = dbopen(false);
if(db == NULL)
{
log_warn("Failed to open database in networkTable_readDevices()");
return false;
}
const char *sql_msg = NULL;
if(!networkTable_readDevices(db, &device_stmt, &sql_msg))
{
// Add SQL message (may be NULL = not available)
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table",
sql_msg);
}
// Read record for a single device
cJSON *devices = JSON_NEW_ARRAY();
network_record network;
unsigned int device_counter = 1;
while(networkTable_readDevicesGetRecord(device_stmt, &network, &sql_msg))
{
cJSON *item = JSON_NEW_OBJECT();
JSON_ADD_NUMBER_TO_OBJECT(item, "id", network.id);
JSON_COPY_STR_TO_OBJECT(item, "hwaddr", network.hwaddr);
JSON_COPY_STR_TO_OBJECT(item, "interface", network.iface);
JSON_ADD_NUMBER_TO_OBJECT(item, "firstSeen", network.firstSeen);
JSON_ADD_NUMBER_TO_OBJECT(item, "lastQuery", network.lastQuery);
JSON_ADD_NUMBER_TO_OBJECT(item, "numQueries", network.numQueries);
JSON_COPY_STR_TO_OBJECT(item, "macVendor", network.macVendor);
// Build array of all IP addresses known associated to this client
cJSON *ips = JSON_NEW_ARRAY();
if(networkTable_readIPs(db, &ip_stmt, network.id, &sql_msg))
{
// Walk known IP addresses + names
network_addresses_record network_address;
unsigned int address_counter = 1;
while(networkTable_readIPsGetRecord(ip_stmt, &network_address, &sql_msg))
{
cJSON *ip = JSON_NEW_OBJECT();
JSON_COPY_STR_TO_OBJECT(ip, "ip", network_address.ip);
JSON_COPY_STR_TO_OBJECT(ip, "name", network_address.name);
JSON_ADD_NUMBER_TO_OBJECT(ip, "lastSeen", network_address.lastSeen);
JSON_ADD_NUMBER_TO_OBJECT(ip, "nameUpdated", network_address.nameUpdated);
JSON_ADD_ITEM_TO_ARRAY(ips, ip);
if(++address_counter > address_count)
break;
}
// Possible error handling
if(sql_msg != NULL)
{
cJSON_Delete(ips);
cJSON_Delete(devices);
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (getting IP records)",
sql_msg);
}
// Finalize sub-query
networkTable_readIPsFinalize(ip_stmt);
if(++device_counter > device_count)
break;
}
// Add array of IP addresses to device
JSON_ADD_ITEM_TO_OBJECT(item, "ips", ips);
// Add device to array of all devices
JSON_ADD_ITEM_TO_ARRAY(devices, item);
}
if(sql_msg != NULL)
{
cJSON_Delete(devices);
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (step)",
sql_msg);
}
// Finalize query
networkTable_readDevicesFinalize(device_stmt);
dbclose(&db);
// Return data to user
cJSON *json = JSON_NEW_OBJECT();
JSON_ADD_ITEM_TO_OBJECT(json, "devices", devices);
JSON_SEND_OBJECT(json);
}

View File

@@ -71,9 +71,7 @@ int api_queries_suggestions(struct ftl_conn *api)
int rc;
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Lock shared memory
lock_shm();
@@ -206,9 +204,7 @@ int api_queries(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Lock shared memory
lock_shm();

View File

@@ -142,9 +142,7 @@ int api_stats_top_domains(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
if(config.misc.privacylevel >= PRIVACY_HIDE_DOMAINS)
@@ -300,9 +298,7 @@ int api_stats_top_clients(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
if(config.misc.privacylevel >= PRIVACY_HIDE_DOMAINS_CLIENTS)
@@ -429,9 +425,7 @@ int api_stats_upstreams(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Lock shared memory
lock_shm();
@@ -539,9 +533,7 @@ int api_stats_query_types(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
lock_shm();
@@ -566,9 +558,7 @@ int api_stats_recentblocked(struct ftl_conn *api)
// Verify requesting client is allowed to see this ressource
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
if(config.misc.privacylevel >= PRIVACY_HIDE_DOMAINS)

View File

@@ -103,6 +103,8 @@ int send_http_internal_error(struct ftl_conn *api)
bool get_bool_var(const char *source, const char *var, bool *boolean)
{
char buffer[16] = { 0 };
if(!source)
return false;
if(GET_VAR(var, buffer, source) > 0)
{
*boolean = (strcasecmp(buffer, "true") == 0);
@@ -204,6 +206,8 @@ bool get_int_var_msg(const char *source, const char *var, int *num, const char *
bool get_int_var(const char *source, const char *var, int *num)
{
const char *msg = NULL;
if(!source)
return false;
return get_int_var_msg(source, var, num, &msg);
}
@@ -232,12 +236,16 @@ bool get_uint_var_msg(const char *source, const char *var, unsigned int *num, co
bool get_uint_var(const char *source, const char *var, unsigned int *num)
{
const char *msg = NULL;
if(!source)
return false;
return get_uint_var_msg(source, var, num, &msg);
}
bool get_double_var_msg(const char *source, const char *var, double *num, const char **msg)
{
char buffer[128] = { 0 };
if(!source)
return false;
if(GET_VAR(var, buffer, source) < 1)
{
// Parameter not found
@@ -271,6 +279,8 @@ bool get_double_var_msg(const char *source, const char *var, double *num, const
bool get_double_var(const char *source, const char *var, double *num)
{
const char *msg = NULL;
if(!source)
return false;
return get_double_var_msg(source, var, num, &msg);
}

View File

@@ -40,9 +40,9 @@ except Exception as e:
exit(1)
# Resolve a reference
def resolveSingleReference(ref: str, k: str):
def resolveSingleReference(ref_str: str, k: str):
# Read and parse the referenced file
ref = ref.partition("#")
ref = ref_str.partition("#")
if len(ref[0]) == 0:
# Empty references are not allowed
raise Exception("Empty reference, always specify a file in the API specification")
@@ -57,12 +57,12 @@ def resolveSingleReference(ref: str, k: str):
# Reduce to what we want to import
for x in ref[2].split("/"):
if len(x) > 0:
#if x not in refYML:
refYML = refYML[x]
return refYML
except Exception as e:
print("Exception when reading " + file + ": " + e)
print("Tried to read" + ref + " in:\n" + json.dumps(refYML, indent=2))
print("Tried to resolve " + k + " pointing to " + ref)
print("Exception when reading " + file + ": " + str(e))
print("Tried to resolve " + ref_str + " in:\n" + json.dumps(refYML, indent=2))
exit(1)
# Recursively resolve references, this can take a few seconds