Introduce new ftl_conn struct that makes it easier to share stuff across processing subroutines in the same thread.

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2021-01-21 16:24:16 +01:00
parent 15ba45e50f
commit f67becc3e1
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
16 changed files with 466 additions and 515 deletions

View File

@ -75,14 +75,11 @@ static void sha256_hex(uint8_t *data, char *buffer)
// Returns >= 0 for any valid authentication
#define LOCALHOSTv4 "127.0.0.1"
#define LOCALHOSTv6 "::1"
int check_client_auth(struct mg_connection *conn, char payload[MAX_PAYLOAD_BYTES])
int check_client_auth(struct ftl_conn *api)
{
int user_id = -1;
const struct mg_request_info *request = mg_get_request_info(conn);
// Is the user requesting from localhost?
if(!httpsettings.api_auth_for_localhost && (strcmp(request->remote_addr, LOCALHOSTv4) == 0 ||
strcmp(request->remote_addr, LOCALHOSTv6) == 0))
if(!httpsettings.api_auth_for_localhost && (strcmp(api->request->remote_addr, LOCALHOSTv4) == 0 ||
strcmp(api->request->remote_addr, LOCALHOSTv6) == 0))
{
return API_AUTH_LOCALHOST;
}
@ -97,15 +94,13 @@ int check_client_auth(struct mg_connection *conn, char payload[MAX_PAYLOAD_BYTES
// Does the client provide a session cookie?
char sid[SID_SIZE];
const char *sid_source = "cookie";
bool sid_avail = http_get_cookie_str(conn, "sid", sid, SID_SIZE);
bool sid_avail = http_get_cookie_str(api, "sid", sid, SID_SIZE);
// If not, does the client provide a session ID via GET/POST?
bool sid_raw_payload = false, sid_json_payload = false;
cJSON *json;
if(!sid_avail && http_get_payload(conn, payload))
if(!sid_avail && api->payload.avail)
{
// Try to extract SID from form-encoded payload
if(GET_VAR("sid", sid, payload) > 0)
if(GET_VAR("sid", sid, api->payload.raw) > 0)
{
// "+" may have been replaced by " ", undo this here
for(unsigned int i = 0; i < SID_SIZE; i++)
@ -118,70 +113,73 @@ int check_client_auth(struct mg_connection *conn, char payload[MAX_PAYLOAD_BYTES
sid_avail = true;
}
// Try to extract SID from root of a possibly included JSON payload
else if((json = cJSON_Parse(payload)) != NULL)
else if(api->payload.json != NULL)
{
cJSON *sid_obj = cJSON_GetObjectItem(json, "sid");
cJSON *sid_obj = cJSON_GetObjectItem(api->payload.json, "sid");
if(cJSON_IsString(sid_obj))
{
strncpy(sid, sid_obj->valuestring, SID_SIZE - 1u);
sid[SID_SIZE-1] = '\0';
sid_source = "payload (JSON)";
sid_avail = true;
}
}
}
logg("payload: \"%s\"", payload);
logg("sid: \"%s\"", sid);
if(sid_avail || sid_raw_payload || sid_json_payload)
if(!sid_avail)
{
const time_t now = time(NULL);
if(config.debug & DEBUG_API)
logg("API: Read sid=\"%s\" from %s", sid, sid_source);
logg("API Authentification: FAIL (no SID provided)");
for(unsigned int i = 0; i < API_MAX_CLIENTS; i++)
return API_AUTH_UNAUTHORIZED;
}
// else: Analyze SID
int user_id = API_AUTH_UNAUTHORIZED;
const time_t now = time(NULL);
if(config.debug & DEBUG_API)
logg("API: Read sid=\"%s\" from %s", sid, sid_source);
for(unsigned int i = 0; i < API_MAX_CLIENTS; i++)
{
if(auth_data[i].used &&
auth_data[i].valid_until >= now &&
strcmp(auth_data[i].remote_addr, api->request->remote_addr) == 0 &&
strcmp(auth_data[i].sid, sid) == 0)
{
if(auth_data[i].used &&
auth_data[i].valid_until >= now &&
strcmp(auth_data[i].remote_addr, request->remote_addr) == 0 &&
strcmp(auth_data[i].sid, sid) == 0)
{
user_id = i;
break;
}
user_id = i;
break;
}
if(user_id > API_AUTH_UNAUTHORIZED)
}
if(user_id > API_AUTH_UNAUTHORIZED)
{
// Authentication succesful:
// - We know this client
// - The session is (still) valid
// - The IP matches the one we know for this SID
// Update timestamp of this client to extend
// the validity of their API authentication
auth_data[user_id].valid_until = now + httpsettings.session_timeout;
// Update user cookie
if(snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
FTL_SET_COOKIE,
auth_data[user_id].sid, httpsettings.session_timeout) < 0)
{
// Authentication succesful:
// - We know this client
// - The session is (still) valid
// - The IP matches the one we know for this SID
// Update timestamp of this client to extend
// the validity of their API authentication
auth_data[user_id].valid_until = now + httpsettings.session_timeout;
// Update user cookie
if(snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
FTL_SET_COOKIE,
auth_data[user_id].sid, httpsettings.session_timeout) < 0)
{
return send_json_error(conn, 500, "internal_error", "Internal server error", NULL);
}
if(config.debug & DEBUG_API)
{
char timestr[128];
get_timestr(timestr, auth_data[user_id].valid_until);
logg("API: Recognized known user: user_id %i valid_until: %s remote_addr %s",
user_id, timestr, auth_data[user_id].remote_addr);
}
return send_json_error(api, 500, "internal_error", "Internal server error", NULL);
}
if(config.debug & DEBUG_API)
{
char timestr[128];
get_timestr(timestr, auth_data[user_id].valid_until);
logg("API: Recognized known user: user_id %i valid_until: %s remote_addr %s",
user_id, timestr, auth_data[user_id].remote_addr);
}
else if(config.debug & DEBUG_API)
logg("API Authentification: FAIL (SID invalid/expired)");
}
else if(config.debug & DEBUG_API)
logg("API Authentification: FAIL (no SID provided)");
logg("API Authentification: FAIL (SID invalid/expired)");
return user_id;
}
@ -209,7 +207,7 @@ static bool check_response(const char *response, const time_t now)
return false;
}
static int get_session_object(struct mg_connection *conn, cJSON *json, const int user_id, const time_t now)
static int get_session_object(struct ftl_conn *api, cJSON *json, const int user_id, const time_t now)
{
// Authentication not needed
if(user_id == API_AUTH_LOCALHOST || user_id == API_AUTH_EMPTYPASS)
@ -254,7 +252,7 @@ static void delete_session(const int user_id)
memset(auth_data[user_id].remote_addr, 0, sizeof(auth_data[user_id].remote_addr));
}
static int send_api_auth_status(struct mg_connection *conn, const int user_id, const int method, const time_t now)
static int send_api_auth_status(struct ftl_conn *api, const int user_id, const time_t now)
{
if(user_id == API_AUTH_LOCALHOST)
{
@ -262,7 +260,7 @@ static int send_api_auth_status(struct mg_connection *conn, const int user_id, c
logg("API Auth status: OK (localhost does not need auth)");
cJSON *json = JSON_NEW_OBJ();
get_session_object(conn, json, user_id, now);
get_session_object(api, json, user_id, now);
JSON_SEND_OBJECT(json);
}
@ -272,11 +270,11 @@ static int send_api_auth_status(struct mg_connection *conn, const int user_id, c
logg("API Auth status: OK (empty password)");
cJSON *json = JSON_NEW_OBJ();
get_session_object(conn, json, user_id, now);
get_session_object(api, json, user_id, now);
JSON_SEND_OBJECT(json);
}
if(user_id > API_AUTH_UNAUTHORIZED && (method == HTTP_GET || method == HTTP_POST))
if(user_id > API_AUTH_UNAUTHORIZED && (api->method == HTTP_GET || api->method == HTTP_POST))
{
if(config.debug & DEBUG_API)
logg("API Auth status: OK");
@ -286,14 +284,14 @@ static int send_api_auth_status(struct mg_connection *conn, const int user_id, c
FTL_SET_COOKIE,
auth_data[user_id].sid, API_SESSION_EXPIRE) < 0)
{
return send_json_error(conn, 500, "internal_error", "Internal server error", NULL);
return send_json_error(api, 500, "internal_error", "Internal server error", NULL);
}
cJSON *json = JSON_NEW_OBJ();
get_session_object(conn, json, user_id, now);
get_session_object(api, json, user_id, now);
JSON_SEND_OBJECT(json);
}
else if(user_id > API_AUTH_UNAUTHORIZED && method == HTTP_DELETE)
else if(user_id > API_AUTH_UNAUTHORIZED && api->method == HTTP_DELETE)
{
if(config.debug & DEBUG_API)
logg("API Auth status: Logout, asking to delete cookie");
@ -303,7 +301,7 @@ static int send_api_auth_status(struct mg_connection *conn, const int user_id, c
strncpy(pi_hole_extra_headers, FTL_DELETE_COOKIE, sizeof(pi_hole_extra_headers));
cJSON *json = JSON_NEW_OBJ();
get_session_object(conn, json, user_id, now);
get_session_object(api, json, user_id, now);
JSON_SEND_OBJECT_CODE(json, 410); // 410 Gone
}
else
@ -313,7 +311,7 @@ static int send_api_auth_status(struct mg_connection *conn, const int user_id, c
strncpy(pi_hole_extra_headers, FTL_DELETE_COOKIE, sizeof(pi_hole_extra_headers));
cJSON *json = JSON_NEW_OBJ();
get_session_object(conn, json, user_id, now);
get_session_object(api, json, user_id, now);
JSON_SEND_OBJECT_CODE(json, 401); // 401 Unauthorized
}
}
@ -378,40 +376,37 @@ static void generateSID(char *sid)
// GET: Check authentication and obtain a challenge
// POST: Login
// DELETE: Logout
int api_auth(struct mg_connection *conn)
int api_auth(struct ftl_conn *api)
{
// Check HTTP method
const enum http_method method = http_method(conn);
const time_t now = time(NULL);
char *password_hash = get_password_hash();
const bool empty_password = (strlen(password_hash) == 0u);
int user_id = API_AUTH_UNAUTHORIZED;
const struct mg_request_info *request = mg_get_request_info(conn);
bool reponse_set = false;
char response[256] = { 0 };
char payload[MAX_PAYLOAD_BYTES];
// Did the client authenticate before and we can validate this?
user_id = check_client_auth(conn, payload);
user_id = check_client_auth(api);
// If this is a valid session, we can exit early at this point
if(user_id != API_AUTH_UNAUTHORIZED)
return send_api_auth_status(conn, user_id, method, now);
return send_api_auth_status(api, user_id, now);
// Login attempt, extract response
if(method == HTTP_POST)
if(api->method == HTTP_POST)
{
// Try to extract response from payload
int len = 0;
if((len = GET_VAR("response", response, payload)) != CHALLENGE_SIZE)
if((len = GET_VAR("response", response, api->payload.raw)) != CHALLENGE_SIZE)
{
const char *message = len < 0 ? "No response found" : "Invalid response length";
if(config.debug & DEBUG_API)
logg("API auth error: %s", message);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
message,
NULL);
@ -420,11 +415,11 @@ int api_auth(struct mg_connection *conn)
}
// Logout attempt
if(method == HTTP_DELETE)
if(api->method == HTTP_DELETE)
{
if(config.debug & DEBUG_API)
logg("API Auth: User with ID %i wants to log out", user_id);
return send_api_auth_status(conn, user_id, method, now);
return send_api_auth_status(api, user_id, now);
}
// Login attempt and/or auth check
@ -455,7 +450,7 @@ int api_auth(struct mg_connection *conn)
{
auth_data[i].used = true;
auth_data[i].valid_until = now + httpsettings.session_timeout;
strncpy(auth_data[i].remote_addr, request->remote_addr, sizeof(auth_data[i].remote_addr));
strncpy(auth_data[i].remote_addr, api->request->remote_addr, sizeof(auth_data[i].remote_addr));
auth_data[i].remote_addr[sizeof(auth_data[i].remote_addr)-1] = '\0';
generateSID(auth_data[i].sid);
@ -486,7 +481,7 @@ int api_auth(struct mg_connection *conn)
// Free allocated memory
free(password_hash);
password_hash = NULL;
return send_api_auth_status(conn, user_id, method, now);
return send_api_auth_status(api, user_id, now);
}
else
{
@ -534,7 +529,7 @@ int api_auth(struct mg_connection *conn)
// Return to user
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "challenge", challenges[i].challenge);
get_session_object(conn, json, -1, now);
get_session_object(api, json, -1, now);
JSON_SEND_OBJECT(json);
}
}

View File

@ -18,7 +18,7 @@
// set_blockingmode_timer()
#include "../timers.h"
static int get_blocking(struct mg_connection *conn)
static int get_blocking(struct ftl_conn *api)
{
// Return current status
cJSON *json = JSON_NEW_OBJ();
@ -45,35 +45,24 @@ static int get_blocking(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
static int set_blocking(struct mg_connection *conn)
static int set_blocking(struct ftl_conn *api)
{
// Verify requesting client is allowed to access this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
char buffer[1024] = { 0 };
const int data_len = mg_read(conn, buffer, sizeof(buffer) - 1);
if ((data_len < 1) || (data_len >= (int)sizeof(buffer))) {
return send_json_error(conn, 400,
"bad_request", "No request body data",
NULL);
}
buffer[data_len] = '\0';
cJSON *obj = cJSON_Parse(buffer);
if (obj == NULL) {
return send_json_error(conn, 400,
if (api->payload.json == NULL) {
return send_json_error(api, 400,
"bad_request",
"Invalid request body data",
NULL);
}
cJSON *elem = cJSON_GetObjectItemCaseSensitive(obj, "blocking");
cJSON *elem = cJSON_GetObjectItemCaseSensitive(api->payload.json, "blocking");
if (!cJSON_IsBool(elem)) {
cJSON_Delete(obj);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"No \"blocking\" boolean in body data",
NULL);
@ -82,13 +71,10 @@ static int set_blocking(struct mg_connection *conn)
// Get (optional) delay
int delay = -1;
elem = cJSON_GetObjectItemCaseSensitive(obj, "delay");
elem = cJSON_GetObjectItemCaseSensitive(api->payload.json, "delay");
if (cJSON_IsNumber(elem) && elem->valuedouble > 0.0)
delay = elem->valueint;
// Free memory not needed any longer
cJSON_Delete(obj);
if(target_status == get_blockingstatus())
{
// The blocking status does not need to be changed
@ -107,19 +93,18 @@ static int set_blocking(struct mg_connection *conn)
// Return GET property as result of POST/PUT/PATCH action
// if no error happened above
return get_blocking(conn);
return get_blocking(api);
}
int api_dns_blockingstatus(struct mg_connection *conn)
int api_dns_blockingstatus(struct ftl_conn *api)
{
int method = http_method(conn);
if(method == HTTP_GET)
if(api->method == HTTP_GET)
{
return get_blocking(conn);
return get_blocking(api);
}
else if(method == HTTP_PUT)
else if(api->method == HTTP_PUT)
{
return set_blocking(conn);
return set_blocking(api);
}
else
{
@ -128,12 +113,12 @@ int api_dns_blockingstatus(struct mg_connection *conn)
}
}
int api_dns_cacheinfo(struct mg_connection *conn)
int api_dns_cacheinfo(struct ftl_conn *api)
{
// Verify requesting client is allowed to access this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
cacheinforecord cacheinfo;

View File

@ -29,28 +29,26 @@
// counters
#include "../shmem.h"
int api_ftl_client(struct mg_connection *conn)
int api_ftl_client(struct ftl_conn *api)
{
cJSON *json = JSON_NEW_OBJ();
const struct mg_request_info *request = mg_get_request_info(conn);
// Add client's IP address
JSON_OBJ_REF_STR(json, "remote_addr", request->remote_addr);
JSON_OBJ_REF_STR(json, "remote_addr", api->request->remote_addr);
// Add HTTP version
JSON_OBJ_REF_STR(json, "http_version", request->http_version);
JSON_OBJ_REF_STR(json, "http_version", api->request->http_version);
// Add request method
JSON_OBJ_REF_STR(json, "method", request->request_method);
JSON_OBJ_REF_STR(json, "method", api->request->request_method);
// Add HTTP headers
cJSON *headers = JSON_NEW_ARRAY();
for(int i = 0; i < request->num_headers; i++)
for(int i = 0; i < api->request->num_headers; i++)
{
// Add headers
cJSON *header = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(header, "name", request->http_headers[i].name);
JSON_OBJ_REF_STR(header, "value", request->http_headers[i].value);
JSON_OBJ_REF_STR(header, "name", api->request->http_headers[i].name);
JSON_OBJ_REF_STR(header, "value", api->request->http_headers[i].value);
JSON_ARRAY_ADD_ITEM(headers, header);
}
JSON_OBJ_ADD_ITEM(json, "headers", headers);
@ -60,21 +58,20 @@ int api_ftl_client(struct mg_connection *conn)
// fifologData is allocated in shared memory for cross-fork compatibility
fifologData *fifo_log = NULL;
int api_ftl_dnsmasq_log(struct mg_connection *conn)
int api_ftl_dnsmasq_log(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
unsigned int start = 0u;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
// Does the user request an ID to sent from?
unsigned int nextID;
if(get_uint_var(request->query_string, "nextID", &nextID))
if(get_uint_var(api->request->query_string, "nextID", &nextID))
{
if(nextID >= fifo_log->next_id)
{
@ -125,12 +122,12 @@ int api_ftl_dnsmasq_log(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_ftl_database(struct mg_connection *conn)
int api_ftl_database(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
send_json_unauthorized(conn);
send_json_unauthorized(api);
}
cJSON *json = JSON_NEW_OBJ();
@ -200,7 +197,7 @@ int api_ftl_database(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
static int read_temp_sensor(struct mg_connection *conn,
static int read_temp_sensor(struct ftl_conn *api,
const char *label_path,
const char *value_path,
const char *fallback_label,
@ -276,12 +273,12 @@ static bool GetRamInKB(long *mem_total, long *mem_used, long *mem_free, long *me
return true;
}
int get_system_obj(struct mg_connection *conn, cJSON *system)
int get_system_obj(struct ftl_conn *api, cJSON *system)
{
const int nprocs = get_nprocs();
struct sysinfo info;
if(sysinfo(&info) != 0)
return send_json_error(conn, 500, "error", strerror(errno), NULL);
return send_json_error(api, 500, "error", strerror(errno), NULL);
// Seconds since boot
JSON_OBJ_ADD_NUMBER(system, "uptime", info.uptime);
@ -362,7 +359,7 @@ int get_system_obj(struct mg_connection *conn, cJSON *system)
sprintf(label_path, "/sys/class/thermal/thermal_zone%d/type", i);
sprintf(value_path, "/sys/class/thermal/thermal_zone%d/temp", i);
sprintf(fallback_label, "thermal_zone%d/temp", i);
ret = read_temp_sensor(conn, label_path, value_path, fallback_label, sensors);
ret = read_temp_sensor(api, label_path, value_path, fallback_label, sensors);
// Error handling
if(ret != 0)
return ret;
@ -371,7 +368,7 @@ int get_system_obj(struct mg_connection *conn, cJSON *system)
sprintf(label_path, "/sys/class/hwmon/hwmon0/temp%d_label", i);
sprintf(value_path, "/sys/class/hwmon/hwmon0/temp%d_input", i);
sprintf(fallback_label, "hwmon0/temp%d", i);
ret = read_temp_sensor(conn, label_path, value_path, fallback_label, sensors);
ret = read_temp_sensor(api, label_path, value_path, fallback_label, sensors);
// Error handling
if(ret != 0)
return ret;
@ -387,13 +384,13 @@ int get_system_obj(struct mg_connection *conn, cJSON *system)
return 0;
}
int api_ftl_system(struct mg_connection *conn)
int api_ftl_system(struct ftl_conn *api)
{
cJSON *json = JSON_NEW_OBJ();
cJSON *system = JSON_NEW_OBJ();
// Get system object
const int ret = get_system_obj(conn, system);
const int ret = get_system_obj(api, system);
if (ret != 0)
return ret;

View File

@ -14,7 +14,7 @@
#include "routes.h"
#include "../database/gravity-db.h"
static int api_list_read(struct mg_connection *conn,
static int api_list_read(struct ftl_conn *api,
const int code,
const enum gravity_list_type listtype,
const char *argument)
@ -34,7 +34,7 @@ static int api_list_read(struct mg_connection *conn,
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
return send_json_error(conn, 400, // 400 Bad Request
return send_json_error(api, 400, // 400 Bad Request
"database_error",
"Could not read domains from database table",
json);
@ -46,7 +46,6 @@ static int api_list_read(struct mg_connection *conn,
{
cJSON *item = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(item, "id", row.id);
JSON_OBJ_ADD_BOOL(item, "enabled", row.enabled);
// Special fields
if(listtype == GRAVITY_GROUPS)
@ -69,8 +68,8 @@ static int api_list_read(struct mg_connection *conn,
}
else // domainlists
{
JSON_OBJ_REF_STR(item, "type", row.type);
JSON_OBJ_COPY_STR(item, "domain", row.domain);
JSON_OBJ_REF_STR(item, "type", row.type);
if(row.comment != NULL) {
JSON_OBJ_COPY_STR(item, "comment", row.comment);
} else {
@ -87,14 +86,15 @@ static int api_list_read(struct mg_connection *conn,
group_ids_str[sizeof(group_ids_str)-2u] = ']';
group_ids_str[sizeof(group_ids_str)-1u] = '\0';
cJSON * group_ids = cJSON_Parse(group_ids_str);
JSON_OBJ_ADD_ITEM(item, "group_ids", group_ids);
JSON_OBJ_ADD_ITEM(item, "groups", group_ids);
} else {
// Empty group set
cJSON *group_ids = JSON_NEW_ARRAY();
JSON_OBJ_ADD_ITEM(item, "group_ids", group_ids);
JSON_OBJ_ADD_ITEM(item, "groups", group_ids);
}
}
JSON_OBJ_ADD_BOOL(item, "enabled", row.enabled);
JSON_OBJ_ADD_NUMBER(item, "date_added", row.date_added);
JSON_OBJ_ADD_NUMBER(item, "date_modified", row.date_modified);
@ -131,16 +131,15 @@ static int api_list_read(struct mg_connection *conn,
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
return send_json_error(conn, 400, // 400 Bad Request
return send_json_error(api, 400, // 400 Bad Request
"database_error",
"Could not read from gravity database",
json);
}
}
static int api_list_write(struct mg_connection *conn,
static int api_list_write(struct ftl_conn *api,
const enum gravity_list_type listtype,
const enum http_method method,
const char *argument,
char payload[MAX_PAYLOAD_BYTES])
{
@ -149,44 +148,36 @@ static int api_list_write(struct mg_connection *conn,
// Set argument
row.argument = argument;
// Extract payload
cJSON *obj = cJSON_Parse(payload);
if (obj == NULL) {
return send_json_error(conn, 400,
// Check if valid JSON payload is available
if (api->payload.json == NULL) {
return send_json_error(api, 400,
"bad_request",
"Invalid request body data",
NULL);
}
cJSON *json_enabled = cJSON_GetObjectItemCaseSensitive(obj, "enabled");
cJSON *json_enabled = cJSON_GetObjectItemCaseSensitive(api->payload.json, "enabled");
if (!cJSON_IsBool(json_enabled)) {
cJSON_Delete(obj);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"No \"enabled\" boolean in body data",
NULL);
}
row.enabled = cJSON_IsTrue(json_enabled);
cJSON *json_comment = cJSON_GetObjectItemCaseSensitive(obj, "comment");
cJSON *json_comment = cJSON_GetObjectItemCaseSensitive(api->payload.json, "comment");
if(cJSON_IsString(json_comment) && strlen(json_comment->valuestring) > 0)
row.comment = json_comment->valuestring;
else
row.comment = NULL;
cJSON *json_description = cJSON_GetObjectItemCaseSensitive(obj, "description");
cJSON *json_description = cJSON_GetObjectItemCaseSensitive(api->payload.json, "description");
if(cJSON_IsString(json_description) && strlen(json_description->valuestring) > 0)
row.description = json_description->valuestring;
else
row.description = NULL;
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(obj, "name");
if(cJSON_IsString(json_name) && strlen(json_name->valuestring) > 0)
row.name = json_name->valuestring;
else
row.name = NULL;
cJSON *json_oldtype = cJSON_GetObjectItemCaseSensitive(obj, "oldtype");
cJSON *json_oldtype = cJSON_GetObjectItemCaseSensitive(api->payload.json, "oldtype");
if(cJSON_IsString(json_oldtype) && strlen(json_oldtype->valuestring) > 0)
row.oldtype = json_oldtype->valuestring;
else
@ -195,9 +186,9 @@ static int api_list_write(struct mg_connection *conn,
// Try to add domain to table
const char *sql_msg = NULL;
bool okay = false;
if(gravityDB_addToTable(listtype, &row, &sql_msg, method))
if(gravityDB_addToTable(listtype, &row, &sql_msg, api->method))
{
cJSON *groups = cJSON_GetObjectItemCaseSensitive(obj, "groups");
cJSON *groups = cJSON_GetObjectItemCaseSensitive(api->payload.json, "groups");
if(groups != NULL)
okay = gravityDB_edit_groups(listtype, groups, &row, &sql_msg);
else
@ -227,25 +218,22 @@ static int api_list_write(struct mg_connection *conn,
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
// Free memory not needed any longer
cJSON_Delete(obj);
// Send error reply
return send_json_error(conn, 400, // 400 Bad Request
return send_json_error(api, 400, // 400 Bad Request
"database_error",
"Could not add to gravity database",
json);
}
// else: everything is okay
// Free memory not needed any longer
cJSON_Delete(obj);
// Send GET style reply with code 201 Created
return api_list_read(conn, 201, listtype, argument);
int response_code = 201; // 201 - Created
if(api->method == HTTP_PUT)
response_code = 200; // 200 - OK
// Send GET style reply
return api_list_read(api, response_code, listtype, argument);
}
static int api_list_remove(struct mg_connection *conn,
static int api_list_remove(struct ftl_conn *api,
const enum gravity_list_type listtype,
const char *argument)
{
@ -269,49 +257,48 @@ static int api_list_remove(struct mg_connection *conn,
}
// Send error reply
return send_json_error(conn, 400,
return send_json_error(api, 400,
"database_error",
"Could not remove domain from database table",
json);
}
}
int api_list(struct mg_connection *conn)
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(conn, payload) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
enum gravity_list_type listtype;
bool can_modify = false;
const struct mg_request_info *request = mg_get_request_info(conn);
const char *argument = NULL;
if((argument = startsWith("/api/groups", request->local_uri)) != NULL)
if((argument = startsWith("/api/groups", api)) != NULL)
{
listtype = GRAVITY_GROUPS;
can_modify = true;
}
else if((argument = startsWith("/api/adlists", request->local_uri)) != NULL)
else if((argument = startsWith("/api/adlists", api)) != NULL)
{
listtype = GRAVITY_ADLISTS;
can_modify = true;
}
else if((argument = startsWith("/api/clients", request->local_uri)) != NULL)
else if((argument = startsWith("/api/clients", api)) != NULL)
{
listtype = GRAVITY_CLIENTS;
can_modify = true;
}
else if((argument = startsWith("/api/domains/allow", request->local_uri)) != NULL)
else if((argument = startsWith("/api/domains/allow", api)) != NULL)
{
if((argument = startsWith("/api/domains/allow/exact", request->local_uri)) != NULL)
if((argument = startsWith("/api/domains/allow/exact", api)) != NULL)
{
listtype = GRAVITY_DOMAINLIST_ALLOW_EXACT;
can_modify = true;
}
else if((argument = startsWith("/api/domains/allow/regex", request->local_uri)) != NULL)
else if((argument = startsWith("/api/domains/allow/regex", api)) != NULL)
{
listtype = GRAVITY_DOMAINLIST_ALLOW_REGEX;
can_modify = true;
@ -319,14 +306,14 @@ int api_list(struct mg_connection *conn)
else
listtype = GRAVITY_DOMAINLIST_ALLOW_ALL;
}
else if((argument = startsWith("/api/domains/deny", request->local_uri)) != NULL)
else if((argument = startsWith("/api/domains/deny", api)) != NULL)
{
if((argument = startsWith("/api/domains/deny/exact", request->local_uri)) != NULL)
if((argument = startsWith("/api/domains/deny/exact", api)) != NULL)
{
listtype = GRAVITY_DOMAINLIST_DENY_EXACT;
can_modify = true;
}
else if((argument = startsWith("/api/domains/deny/regex", request->local_uri)) != NULL)
else if((argument = startsWith("/api/domains/deny/regex", api)) != NULL)
{
listtype = GRAVITY_DOMAINLIST_DENY_REGEX;
can_modify = true;
@ -336,36 +323,35 @@ int api_list(struct mg_connection *conn)
}
else
{
if((argument = startsWith("/api/domains/exact", request->local_uri)) != NULL)
if((argument = startsWith("/api/domains/exact", api)) != NULL)
listtype = GRAVITY_DOMAINLIST_ALL_EXACT;
else if((argument = startsWith("/api/domains/regex", request->local_uri)) != NULL)
else if((argument = startsWith("/api/domains/regex", api)) != NULL)
listtype = GRAVITY_DOMAINLIST_ALL_REGEX;
else
{
argument = startsWith("/api/domains", request->local_uri);
argument = startsWith("/api/domains", api);
listtype = GRAVITY_DOMAINLIST_ALL_ALL;
}
}
const enum http_method method = http_method(conn);
if(method == HTTP_GET)
if(api->method == HTTP_GET)
{
return api_list_read(conn, 200, listtype, argument);
return api_list_read(api, 200, listtype, argument);
}
else if(can_modify && (method == HTTP_POST || method == HTTP_PUT))
else if(can_modify && (api->method == HTTP_POST || api->method == HTTP_PUT))
{
// Add item from list
return api_list_write(conn, listtype, method, argument, payload);
return api_list_write(api, listtype, argument, payload);
}
else if(can_modify && method == HTTP_DELETE)
else if(can_modify && api->method == HTTP_DELETE)
{
// Delete item from list
return api_list_remove(conn, listtype, argument);
return api_list_remove(api, listtype, argument);
}
else if(!can_modify)
{
// This list type cannot be modified (e.g., ALL_ALL)
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"Invalid request: Specify list to modify",
NULL);

View File

@ -15,15 +15,15 @@
// networkrecord
#include "../database/network-table.h"
int api_network(struct mg_connection *conn)
int api_network(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Connect to database
// apiect to database
const char *sql_msg = NULL;
if(!networkTable_readDevices(&sql_msg))
{
@ -34,7 +34,7 @@ int api_network(struct mg_connection *conn)
} else {
JSON_OBJ_ADD_NULL(json, "sql_msg");
}
return send_json_error(conn, 500,
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table",
json);
@ -73,7 +73,7 @@ int api_network(struct mg_connection *conn)
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,
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (getting IP records)",
json);
@ -96,7 +96,7 @@ int api_network(struct mg_connection *conn)
json = JSON_NEW_OBJ();
// Add SQL message
JSON_OBJ_REF_STR(json, "sql_msg", sql_msg);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"database_error",
"Could not read network details from database table (step)",
json);

View File

@ -24,161 +24,177 @@ int api_handler(struct mg_connection *conn, void *ignored)
int ret = 0;
const struct mg_request_info *request = mg_get_request_info(conn);
// Prepare API info struct
struct ftl_conn api = {
conn,
mg_get_request_info(conn),
http_method(conn),
{ 0 }
};
read_and_parse_payload(&api);
if(config.debug & DEBUG_API)
logg("Requested API URI: %s %s", request->request_method, request->local_uri);
logg("Requested API URI: %s %s", api.request->request_method, api.request->local_uri);
/******************************** /api/dns ********************************/
if(startsWith("/api/dns/blocking", request->local_uri))
if(startsWith("/api/dns/blocking", &api))
{
ret = api_dns_blockingstatus(conn);
ret = api_dns_blockingstatus(&api);
}
else if(startsWith("/api/dns/cacheinfo", request->local_uri))
else if(startsWith("/api/dns/cacheinfo", &api))
{
ret = api_dns_cacheinfo(conn);
ret = api_dns_cacheinfo(&api);
}
/*********** /api/domains, /api/groups, /api/adlists, /api/clients *******/
else if(startsWith("/api/domains", request->local_uri))
else if(startsWith("/api/domains", &api))
{
ret = api_list(conn);
ret = api_list(&api);
}
else if(startsWith("/api/groups", request->local_uri))
else if(startsWith("/api/groups", &api))
{
ret = api_list(conn);
ret = api_list(&api);
}
else if(startsWith("/api/adlists", request->local_uri))
else if(startsWith("/api/adlists", &api))
{
ret = api_list(conn);
ret = api_list(&api);
}
else if(startsWith("/api/clients", request->local_uri))
else if(startsWith("/api/clients", &api))
{
ret = api_list(conn);
ret = api_list(&api);
}
/******************************** /api/ftl ****************************/
else if(startsWith("/api/ftl/client", request->local_uri))
else if(startsWith("/api/ftl/client", &api))
{
ret = api_ftl_client(conn);
ret = api_ftl_client(&api);
}
else if(startsWith("/api/ftl/dnsmasq_log", request->local_uri))
else if(startsWith("/api/ftl/dnsmasq_log", &api))
{
ret = api_ftl_dnsmasq_log(conn);
ret = api_ftl_dnsmasq_log(&api);
}
else if(startsWith("/api/ftl/database", request->local_uri))
else if(startsWith("/api/ftl/database", &api))
{
ret = api_ftl_database(conn);
ret = api_ftl_database(&api);
}
else if(startsWith("/api/ftl/system", request->local_uri))
else if(startsWith("/api/ftl/system", &api))
{
ret = api_ftl_system(conn);
ret = api_ftl_system(&api);
}
/******************************** /api/network ****************************/
else if(startsWith("/api/network", request->local_uri))
else if(startsWith("/api/network", &api))
{
ret = api_network(conn);
ret = api_network(&api);
}
/******************************** /api/stats **************************/
else if(startsWith("/api/stats/summary", request->local_uri))
else if(startsWith("/api/stats/summary", &api))
{
ret = api_stats_summary(conn);
ret = api_stats_summary(&api);
}
else if(startsWith("/api/stats/overTime/history", request->local_uri))
else if(startsWith("/api/stats/overTime/history", &api))
{
ret = api_stats_overTime_history(conn);
ret = api_stats_overTime_history(&api);
}
else if(startsWith("/api/stats/overTime/clients", request->local_uri))
else if(startsWith("/api/stats/overTime/clients", &api))
{
ret = api_stats_overTime_clients(conn);
ret = api_stats_overTime_clients(&api);
}
else if(startsWith("/api/stats/query_types", request->local_uri))
else if(startsWith("/api/stats/query_types", &api))
{
ret = api_stats_query_types(conn);
ret = api_stats_query_types(&api);
}
else if(startsWith("/api/stats/upstreams", request->local_uri))
else if(startsWith("/api/stats/upstreams", &api))
{
ret = api_stats_upstreams(conn);
ret = api_stats_upstreams(&api);
}
else if(startsWith("/api/stats/top_domains", request->local_uri))
else if(startsWith("/api/stats/top_domains", &api))
{
ret = api_stats_top_domains(false, conn);
ret = api_stats_top_domains(false, &api);
}
else if(startsWith("/api/stats/top_blocked", request->local_uri))
else if(startsWith("/api/stats/top_blocked", &api))
{
ret = api_stats_top_domains(true, conn);
ret = api_stats_top_domains(true, &api);
}
else if(startsWith("/api/stats/top_clients", request->local_uri))
else if(startsWith("/api/stats/top_clients", &api))
{
ret = api_stats_top_clients(false, conn);
ret = api_stats_top_clients(false, &api);
}
else if(startsWith("/api/stats/top_blocked_clients", request->local_uri))
else if(startsWith("/api/stats/top_blocked_clients", &api))
{
ret = api_stats_top_clients(true, conn);
ret = api_stats_top_clients(true, &api);
}
else if(startsWith("/api/stats/history", request->local_uri))
else if(startsWith("/api/stats/history", &api))
{
ret = api_stats_history(conn);
ret = api_stats_history(&api);
}
else if(startsWith("/api/stats/recent_blocked", request->local_uri))
else if(startsWith("/api/stats/recent_blocked", &api))
{
ret = api_stats_recentblocked(conn);
ret = api_stats_recentblocked(&api);
}
else if(startsWith("/api/stats/database/overTime/history", request->local_uri))
else if(startsWith("/api/stats/database/overTime/history", &api))
{
ret = api_stats_database_overTime_history(conn);
ret = api_stats_database_overTime_history(&api);
}
else if(startsWith("/api/stats/database/top_domains", request->local_uri))
else if(startsWith("/api/stats/database/top_domains", &api))
{
ret = api_stats_database_top_items(false, true, conn);
ret = api_stats_database_top_items(false, true, &api);
}
else if(startsWith("/api/stats/database/top_blocked", request->local_uri))
else if(startsWith("/api/stats/database/top_blocked", &api))
{
ret = api_stats_database_top_items(true, true, conn);
ret = api_stats_database_top_items(true, true, &api);
}
else if(startsWith("/api/stats/database/top_clients", request->local_uri))
else if(startsWith("/api/stats/database/top_clients", &api))
{
ret = api_stats_database_top_items(false, false, conn);
ret = api_stats_database_top_items(false, false, &api);
}
else if(startsWith("/api/stats/database/summary", request->local_uri))
else if(startsWith("/api/stats/database/summary", &api))
{
ret = api_stats_database_summary(conn);
ret = api_stats_database_summary(&api);
}
else if(startsWith("/api/stats/database/overTime/clients", request->local_uri))
else if(startsWith("/api/stats/database/overTime/clients", &api))
{
ret = api_stats_database_overTime_clients(conn);
ret = api_stats_database_overTime_clients(&api);
}
else if(startsWith("/api/stats/database/query_types", request->local_uri))
else if(startsWith("/api/stats/database/query_types", &api))
{
ret = api_stats_database_query_types(conn);
ret = api_stats_database_query_types(&api);
}
else if(startsWith("/api/stats/database/upstreams", request->local_uri))
else if(startsWith("/api/stats/database/upstreams", &api))
{
ret = api_stats_database_upstreams(conn);
ret = api_stats_database_upstreams(&api);
}
/******************************** /api/version ****************************/
else if(startsWith("/api/version", request->local_uri))
else if(startsWith("/api/version", &api))
{
ret = api_version(conn);
ret = api_version(&api);
}
/******************************** /api/auth ****************************/
else if(startsWith("/api/auth", request->local_uri))
else if(startsWith("/api/auth", &api))
{
ret = api_auth(conn);
ret = api_auth(&api);
}
/******************************** /api/settings ****************************/
else if(startsWith("/api/settings/web", request->local_uri))
else if(startsWith("/api/settings/web", &api))
{
ret = api_settings_web(conn);
ret = api_settings_web(&api);
}
/******************************** not found or invalid request**************/
if(ret == 0)
{
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "path", request->local_uri);
ret = send_json_error(conn, 404,
cJSON *string_item = cJSON_CreateStringReference((const char*)api.request->local_uri);
cJSON_AddItemToObject(json, "path", string_item);
ret = send_json_error(&api, 404,
"not_found",
"Not found",
json);
}
// Free JSON-parsed payload memory (if allocated)
if(api.payload.json != NULL)
{
cJSON_Delete(api.payload.json);
api.payload.json = NULL;
}
// Unlock after API access
unlock_shm();

View File

@ -19,51 +19,52 @@
// API router
int api_handler(struct mg_connection *conn, void *ignored);
// Statistic methods
int api_stats_summary(struct mg_connection *conn);
int api_stats_overTime_history(struct mg_connection *conn);
int api_stats_overTime_clients(struct mg_connection *conn);
int api_stats_query_types(struct mg_connection *conn);
int api_stats_upstreams(struct mg_connection *conn);
int api_stats_top_domains(bool blocked, struct mg_connection *conn);
int api_stats_top_clients(bool blocked, struct mg_connection *conn);
int api_stats_history(struct mg_connection *conn);
int api_stats_recentblocked(struct mg_connection *conn);
int api_stats_summary(struct ftl_conn *api);
int api_stats_overTime_history(struct ftl_conn *api);
int api_stats_overTime_clients(struct ftl_conn *api);
int api_stats_query_types(struct ftl_conn *api);
int api_stats_upstreams(struct ftl_conn *api);
int api_stats_top_domains(bool blocked, struct ftl_conn *api);
int api_stats_top_clients(bool blocked, struct ftl_conn *api);
int api_stats_history(struct ftl_conn *api);
int api_stats_recentblocked(struct ftl_conn *api);
// Statistics methods (database)
int api_stats_database_overTime_history(struct mg_connection *conn);
int api_stats_database_top_items(bool blocked, bool domains, struct mg_connection *conn);
int api_stats_database_summary(struct mg_connection *conn);
int api_stats_database_overTime_clients(struct mg_connection *conn);
int api_stats_database_query_types(struct mg_connection *conn);
int api_stats_database_upstreams(struct mg_connection *conn);
int api_stats_database_overTime_history(struct ftl_conn *api);
int api_stats_database_top_items(bool blocked, bool domains, struct ftl_conn *api);
int api_stats_database_summary(struct ftl_conn *api);
int api_stats_database_overTime_clients(struct ftl_conn *api);
int api_stats_database_query_types(struct ftl_conn *api);
int api_stats_database_upstreams(struct ftl_conn *api);
// FTL methods
int api_ftl_client(struct mg_connection *conn);
int api_ftl_dnsmasq_log(struct mg_connection *conn);
int api_ftl_database(struct mg_connection *conn);
int api_ftl_system(struct mg_connection *conn);
int get_system_obj(struct mg_connection *conn, cJSON *system);
int api_ftl_client(struct ftl_conn *api);
int api_ftl_dnsmasq_log(struct ftl_conn *api);
int api_ftl_database(struct ftl_conn *api);
int api_ftl_system(struct ftl_conn *api);
int get_system_obj(struct ftl_conn *api, cJSON *system);
// Network methods
int api_network(struct mg_connection *conn);
int api_network(struct ftl_conn *api);
// DNS methods
int api_dns_blockingstatus(struct mg_connection *conn);
int api_dns_cacheinfo(struct mg_connection *conn);
int api_dns_blockingstatus(struct ftl_conn *api);
int api_dns_cacheinfo(struct ftl_conn *api);
// List methods
int api_list(struct mg_connection *conn);
int api_group(struct mg_connection *conn);
int api_list(struct ftl_conn *api);
int api_group(struct ftl_conn *api);
// Version method
int api_version(struct mg_connection *conn);
int api_version(struct ftl_conn *api);
// Auth method
int check_client_auth(struct mg_connection *conn, char payload[MAX_PAYLOAD_BYTES]);
int api_auth(struct mg_connection *conn);
int check_client_auth(struct ftl_conn *api);
int api_auth(struct ftl_conn *api);
// Settings methods
int api_settings_web(struct mg_connection *conn);
int api_settings_web(struct ftl_conn *api);
#endif // ROUTES_H

View File

@ -13,7 +13,7 @@
#include "../webserver/json_macros.h"
#include "routes.h"
int api_settings_web(struct mg_connection *conn)
int api_settings_web(struct ftl_conn *api)
{
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "layout", "boxed");

View File

@ -59,7 +59,7 @@ static int __attribute__((pure)) cmpdesc(const void *a, const void *b)
return 0;
}
int api_stats_summary(struct mg_connection *conn)
int api_stats_summary(struct ftl_conn *api)
{
const int blocked = counters->blocked;
const int total = counters->queries;
@ -118,14 +118,14 @@ int api_stats_summary(struct mg_connection *conn)
JSON_OBJ_ADD_ITEM(json, "reply_types", reply_types);
cJSON *system = JSON_NEW_OBJ();
const int ret = get_system_obj(conn, system);
const int ret = get_system_obj(api, system);
if(ret != 0) return ret;
JSON_OBJ_ADD_ITEM(json, "system", system);
JSON_SEND_OBJECT(json);
}
int api_stats_overTime_history(struct mg_connection *conn)
int api_stats_overTime_history(struct ftl_conn *api)
{
int from = 0, until = OVERTIME_SLOTS;
bool found = false;
@ -179,15 +179,15 @@ int api_stats_overTime_history(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_top_domains(bool blocked, struct mg_connection *conn)
int api_stats_top_domains(bool blocked, struct ftl_conn *api)
{
int temparray[counters->domains][2], show = 10;
bool audit = false;
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
@ -207,18 +207,17 @@ int api_stats_top_domains(bool blocked, struct mg_connection *conn)
}
// /api/stats/top_domains?blocked=true is allowed as well
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
// Should blocked clients be shown?
get_bool_var(request->query_string, "blocked", &blocked);
get_bool_var(api->request->query_string, "blocked", &blocked);
// Does the user request a non-default number of replies?
// Note: We do not accept zero query requests here
get_int_var(request->query_string, "show", &show);
get_int_var(api->request->query_string, "show", &show);
// Apply Audit Log filtering?
get_bool_var(request->query_string, "audit", &audit);
get_bool_var(api->request->query_string, "audit", &audit);
}
for(int domainID=0; domainID < counters->domains; domainID++)
@ -337,15 +336,15 @@ int api_stats_top_domains(bool blocked, struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_top_clients(bool blocked, struct mg_connection *conn)
int api_stats_top_clients(bool blocked, struct ftl_conn *api)
{
int temparray[counters->clients][2], show = 10;
bool includezeroclients = false;
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
@ -365,18 +364,17 @@ int api_stats_top_clients(bool blocked, struct mg_connection *conn)
}
// /api/stats/top_clients9?blocked=true is allowed as well
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
// Should blocked clients be shown?
get_bool_var(request->query_string, "blocked", &blocked);
get_bool_var(api->request->query_string, "blocked", &blocked);
// Does the user request a non-default number of replies?
// Note: We do not accept zero query requests here
get_int_var(request->query_string, "show", &show);
get_int_var(api->request->query_string, "show", &show);
// Show also clients which have not been active recently?
get_bool_var(request->query_string, "withzero", &includezeroclients);
get_bool_var(api->request->query_string, "withzero", &includezeroclients);
}
for(int clientID = 0; clientID < counters->clients; clientID++)
@ -465,14 +463,14 @@ int api_stats_top_clients(bool blocked, struct mg_connection *conn)
}
int api_stats_upstreams(struct mg_connection *conn)
int api_stats_upstreams(struct ftl_conn *api)
{
int temparray[counters->forwarded][2];
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
for(int upstreamID = 0; upstreamID < counters->upstreams; upstreamID++)
{
@ -568,12 +566,12 @@ int api_stats_upstreams(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_query_types(struct mg_connection *conn)
int api_stats_query_types(struct ftl_conn *api)
{
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Send response
@ -588,7 +586,7 @@ int api_stats_query_types(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_history(struct mg_connection *conn)
int api_stats_history(struct ftl_conn *api)
{
// Exit before processing any data if requested via config setting
get_privacy_level(NULL);
@ -607,9 +605,9 @@ int api_stats_history(struct mg_connection *conn)
}
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Do we want a more specific version of this command (domain/client/time interval filtered)?
@ -635,25 +633,24 @@ int api_stats_history(struct mg_connection *conn)
// We send 200 queries (until the API is asked for a different limit)
unsigned int show = 200u;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
// Time filtering?
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
// Query type filtering?
int num;
if(get_int_var(request->query_string, "querytype", &num) && num < TYPE_MAX)
if(get_int_var(api->request->query_string, "querytype", &num) && num < TYPE_MAX)
querytype = num;
// Does the user request a non-default number of replies?
// Note: We do not accept zero query requests here
get_uint_var(request->query_string, "show", &show);
get_uint_var(api->request->query_string, "show", &show);
// Forward destination filtering?
char buffer[256] = { 0 };
if(GET_VAR("forward", buffer, request->query_string) > 0)
if(GET_VAR("forward", buffer, api->request->query_string) > 0)
{
forwarddest = calloc(256, sizeof(char));
if(forwarddest == NULL)
@ -712,7 +709,7 @@ int api_stats_history(struct mg_connection *conn)
JSON_OBJ_COPY_STR(json, "upstream", forwarddest);
free(forwarddest);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"Requested upstream not found",
json);
@ -721,7 +718,7 @@ int api_stats_history(struct mg_connection *conn)
}
// Domain filtering?
if(GET_VAR("domain", buffer, request->query_string) > 0)
if(GET_VAR("domain", buffer, api->request->query_string) > 0)
{
domainname = calloc(512, sizeof(char));
if(domainname == NULL)
@ -755,7 +752,7 @@ int api_stats_history(struct mg_connection *conn)
JSON_OBJ_COPY_STR(json, "domain", domainname);
free(domainname);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"Requested domain not found",
json);
@ -763,7 +760,7 @@ int api_stats_history(struct mg_connection *conn)
}
// Client filtering?
if(GET_VAR("client", buffer, request->query_string) > 0)
if(GET_VAR("client", buffer, api->request->query_string) > 0)
{
clientname = calloc(512, sizeof(char));
if(clientname == NULL)
@ -805,7 +802,7 @@ int api_stats_history(struct mg_connection *conn)
JSON_OBJ_COPY_STR(json, "client", clientname);
free(clientname);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"Requested client not found",
json);
@ -813,7 +810,7 @@ int api_stats_history(struct mg_connection *conn)
}
unsigned int unum = 0u;
if(GET_VAR("cursor", buffer, request->query_string) > 0 &&
if(GET_VAR("cursor", buffer, api->request->query_string) > 0 &&
sscanf(buffer, "%u", &unum) > 0)
{
// Do not start at the most recent, but at an older query
@ -830,7 +827,7 @@ int api_stats_history(struct mg_connection *conn)
JSON_OBJ_ADD_NUMBER(json, "maxval", counters->queries);
free(clientname);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"Requested cursor larger than number of queries",
json);
@ -1060,14 +1057,14 @@ int api_stats_history(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_recentblocked(struct mg_connection *conn)
int api_stats_recentblocked(struct ftl_conn *api)
{
unsigned int show = 1;
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Exit before processing any data if requested via config setting
@ -1081,12 +1078,11 @@ int api_stats_recentblocked(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
// Does the user request a non-default number of replies?
// Note: We do not accept zero query requests here
get_uint_var(request->query_string, "show", &show);
get_uint_var(api->request->query_string, "show", &show);
}
// Find most recently blocked query
@ -1124,14 +1120,14 @@ int api_stats_recentblocked(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_overTime_clients(struct mg_connection *conn)
int api_stats_overTime_clients(struct ftl_conn *api)
{
int sendit = -1, until = OVERTIME_SLOTS;
// Verify requesting client is allowed to see this ressource
if(check_client_auth(conn, NULL) == API_AUTH_UNAUTHORIZED)
if(check_client_auth(api) == API_AUTH_UNAUTHORIZED)
{
return send_json_unauthorized(conn);
return send_json_unauthorized(api);
}
// Find minimum ID to send

View File

@ -20,15 +20,14 @@
// FTL_db
#include "../database/common.h"
int api_stats_database_overTime_history(struct mg_connection *conn)
int api_stats_database_overTime_history(struct ftl_conn *api)
{
unsigned int from = 0, until = 0;
const int interval = 600;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
}
// Check if we received the required information
@ -37,7 +36,7 @@ int api_stats_database_overTime_history(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify \"until\" in the request.",
json);
@ -79,7 +78,7 @@ int api_stats_database_overTime_history(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind interval",
json);
@ -100,7 +99,7 @@ int api_stats_database_overTime_history(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind from",
json);
@ -121,7 +120,7 @@ int api_stats_database_overTime_history(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind until",
json);
@ -182,23 +181,22 @@ int api_stats_database_overTime_history(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_database_top_items(bool blocked, bool domains, struct mg_connection *conn)
int api_stats_database_top_items(bool blocked, bool domains, struct ftl_conn *api)
{
unsigned int from = 0, until = 0, show = 10;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
// Get blocked queries not only for .../top_blocked
// but also for .../top_domains?blocked=true
// Note: this may overwrite the blocked propery from the URL
get_bool_var(request->query_string, "blocked", &blocked);
get_bool_var(api->request->query_string, "blocked", &blocked);
// Does the user request a non-default number of replies?
// Note: We do not accept zero query requests here
get_uint_var(request->query_string, "show", &show);
get_uint_var(api->request->query_string, "show", &show);
}
// Check if we received the required information
@ -207,7 +205,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
@ -277,7 +275,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
JSON_OBJ_REF_STR(json, "querystr", querystr);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to prepare query string",
json);
@ -298,7 +296,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind from",
json);
@ -319,7 +317,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind until",
json);
@ -340,7 +338,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind show",
json);
@ -378,14 +376,13 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
JSON_SEND_OBJECT(json);
}
int api_stats_database_summary(struct mg_connection *conn)
int api_stats_database_summary(struct ftl_conn *api)
{
unsigned int from = 0, until = 0;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
}
// Check if we received the required information
@ -394,7 +391,7 @@ int api_stats_database_summary(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
@ -435,7 +432,7 @@ int api_stats_database_summary(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Internal server error",
json);
@ -459,15 +456,14 @@ int api_stats_database_summary(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_database_overTime_clients(struct mg_connection *conn)
int api_stats_database_overTime_clients(struct ftl_conn *api)
{
unsigned int from = 0, until = 0;
const int interval = 600;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
}
// Check if we received the required information
@ -476,7 +472,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
@ -505,7 +501,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to prepare outer statement",
json);
@ -526,7 +522,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind from",
json);
@ -547,7 +543,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind until",
json);
@ -584,7 +580,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to prepare inner statement",
json);
@ -605,7 +601,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind interval",
json);
@ -626,7 +622,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind from",
json);
@ -647,7 +643,7 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind until",
json);
@ -722,14 +718,13 @@ int api_stats_database_overTime_clients(struct mg_connection *conn)
JSON_SEND_OBJECT(json);
}
int api_stats_database_query_types(struct mg_connection *conn)
int api_stats_database_query_types(struct ftl_conn *api)
{
unsigned int from = 0, until = 0;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
}
// Check if we received the required information
@ -738,7 +733,7 @@ int api_stats_database_query_types(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
@ -777,14 +772,13 @@ int api_stats_database_query_types(struct mg_connection *conn)
}
int api_stats_database_upstreams(struct mg_connection *conn)
int api_stats_database_upstreams(struct ftl_conn *api)
{
unsigned int from = 0, until = 0;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
if(api->request->query_string != NULL)
{
get_uint_var(request->query_string, "from", &from);
get_uint_var(request->query_string, "until", &until);
get_uint_var(api->request->query_string, "from", &from);
get_uint_var(api->request->query_string, "until", &until);
}
// Check if we received the required information
@ -793,7 +787,7 @@ int api_stats_database_upstreams(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
return send_json_error(api, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
@ -838,7 +832,7 @@ int api_stats_database_upstreams(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to prepare statement",
json);
@ -859,7 +853,7 @@ int api_stats_database_upstreams(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind from",
json);
@ -880,7 +874,7 @@ int api_stats_database_upstreams(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
return send_json_error(api, 500,
"internal_error",
"Failed to bind until",
json);

View File

@ -16,7 +16,7 @@
#include "../log.h"
#include "../version.h"
int api_version(struct mg_connection *conn)
int api_version(struct ftl_conn *api)
{
cJSON *json = JSON_NEW_OBJ();

View File

@ -30,6 +30,7 @@ typedef struct {
const char *description;
const char *argument;
const char *oldtype;
const char *ip;
long id;
time_t date_added;
time_t date_modified;

View File

@ -41,35 +41,35 @@ const char* json_formatter(const cJSON *object)
}
}
int send_http(struct mg_connection *conn, const char *mime_type,
int send_http(struct ftl_conn *api, const char *mime_type,
const char *msg)
{
mg_send_http_ok(conn, mime_type, NULL, strlen(msg));
return mg_write(conn, msg, strlen(msg));
mg_send_http_ok(api->conn, mime_type, NULL, strlen(msg));
return mg_write(api->conn, msg, strlen(msg));
}
int send_http_code(struct mg_connection *conn, const char *mime_type,
int send_http_code(struct ftl_conn *api, const char *mime_type,
int code, const char *msg)
{
// Payload will be sent with text/plain encoding due to
// the first line being "Error <code>" by definition
//return mg_send_http_error(conn, code, "%s", msg);
my_send_http_error_headers(conn, code,
my_send_http_error_headers(api->conn, code,
mime_type,
strlen(msg));
return mg_write(conn, msg, strlen(msg));
return mg_write(api->conn, msg, strlen(msg));
}
int send_json_unauthorized(struct mg_connection *conn)
int send_json_unauthorized(struct ftl_conn *api)
{
return send_json_error(conn, 401,
return send_json_error(api, 401,
"unauthorized",
"Unauthorized",
NULL);
}
int send_json_error(struct mg_connection *conn, const int code,
int send_json_error(struct ftl_conn *api, const int code,
const char *key, const char* message,
cJSON *data)
{
@ -93,16 +93,16 @@ int send_json_error(struct mg_connection *conn, const int code,
JSON_SEND_OBJECT_CODE(json, code);
}
int send_json_success(struct mg_connection *conn)
int send_json_success(struct ftl_conn *api)
{
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "status", "success");
JSON_SEND_OBJECT(json);
}
int send_http_internal_error(struct mg_connection *conn)
int send_http_internal_error(struct ftl_conn *api)
{
return mg_send_http_error(conn, 500, "Internal server error");
return mg_send_http_error(api->conn, 500, "Internal server error");
}
bool get_bool_var(const char *source, const char *var, bool *boolean)
@ -140,40 +140,12 @@ bool get_uint_var(const char *source, const char *var, unsigned int *num)
return false;
}
// Extract payload either from GET or POST data
bool http_get_payload(struct mg_connection *conn, char *payload)
const char* __attribute__((pure)) startsWith(const char *path, const struct ftl_conn *api)
{
// We do not want to extract any payload here
if(payload == NULL)
return false;
const enum http_method method = http_method(conn);
const struct mg_request_info *request = mg_get_request_info(conn);
if(method == HTTP_GET && request->query_string != NULL)
{
strncpy(payload, request->query_string, MAX_PAYLOAD_BYTES-1);
return true;
}
else // POST, PUT, PATCH
{
int data_len = mg_read(conn, payload, MAX_PAYLOAD_BYTES - 1);
if ((data_len < 1) || (data_len >= MAX_PAYLOAD_BYTES))
return false;
payload[data_len] = '\0';
return true;
}
// Everything else
return false;
}
const char* __attribute__((pure)) startsWith(const char *path, const char *uri)
{
if(strncmp(path, uri, strlen(path)) == 0)
if(uri[strlen(path)] == '/')
if(strncmp(path, api->request->local_uri, strlen(path)) == 0)
if(api->request->local_uri[strlen(path)] == '/')
// Path match with argument after ".../"
return uri + strlen(path) + 1u;
return api->request->local_uri + strlen(path) + 1u;
else
// Path match without argument
return "";
@ -182,11 +154,11 @@ const char* __attribute__((pure)) startsWith(const char *path, const char *uri)
return NULL;
}
bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int *i)
bool http_get_cookie_int(struct ftl_conn *api, const char *cookieName, int *i)
{
// Maximum cookie length is 4KB
char cookieValue[4096];
const char *cookie = mg_get_header(conn, "Cookie");
const char *cookie = mg_get_header(api->conn, "Cookie");
if(mg_get_cookie(cookie, cookieName, cookieValue, sizeof(cookieValue)) > 0)
{
*i = atoi(cookieValue);
@ -195,9 +167,9 @@ bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int
return false;
}
bool http_get_cookie_str(struct mg_connection *conn, const char *cookieName, char *str, size_t str_size)
bool http_get_cookie_str(struct ftl_conn *api, const char *cookieName, char *str, size_t str_size)
{
const char *cookie = mg_get_header(conn, "Cookie");
const char *cookie = mg_get_header(api->conn, "Cookie");
if(mg_get_cookie(cookie, cookieName, str, str_size) > 0)
{
return true;
@ -205,7 +177,7 @@ bool http_get_cookie_str(struct mg_connection *conn, const char *cookieName, cha
return false;
}
int http_method(struct mg_connection *conn)
enum http_method __attribute__((pure)) http_method(struct mg_connection *conn)
{
const struct mg_request_info *request = mg_get_request_info(conn);
if(strcmp(request->request_method, "GET") == 0)
@ -220,17 +192,25 @@ int http_method(struct mg_connection *conn)
return HTTP_UNKNOWN;
}
cJSON *get_POST_JSON(struct mg_connection *conn)
void read_and_parse_payload(struct ftl_conn *api)
{
// Extract payload
char buffer[1024] = { 0 };
int data_len = mg_read(conn, buffer, sizeof(buffer) - 1);
if ((data_len < 1) || (data_len >= (int)sizeof(buffer)))
return NULL;
// Try to extract payload from GET request
if(api->method == HTTP_GET && api->request->query_string != NULL)
{
strncpy(api->payload.raw, api->request->query_string, MAX_PAYLOAD_BYTES-1);
api->payload.avail = true;
}
else // POST, PUT
{
int data_len = mg_read(api->conn, api->payload.raw, MAX_PAYLOAD_BYTES - 1);
logg("Received payload with size: %d", data_len);
if ((data_len < 1) || (data_len >= MAX_PAYLOAD_BYTES))
return;
buffer[data_len] = '\0';
api->payload.raw[data_len] = '\0';
api->payload.avail = true;
// Parse JSON
cJSON *obj = cJSON_Parse(buffer);
return obj;
// Try to parse possibly existing JSON payload
api->payload.json = cJSON_Parse(api->payload.raw);
}
}

View File

@ -17,40 +17,49 @@
// strlen()
#include <string.h>
// API-internal definitions
#define MAX_PAYLOAD_BYTES 2048
enum http_method { HTTP_UNKNOWN, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_DELETE };
struct ftl_conn {
struct mg_connection *conn;
const struct mg_request_info *request;
const enum http_method method;
struct {
bool avail :1;
char raw[MAX_PAYLOAD_BYTES];
cJSON *json;
} payload;
};
const char* json_formatter(const cJSON *object);
int send_http(struct mg_connection *conn, const char *mime_type, const char *msg);
int send_http_code(struct mg_connection *conn, const char *mime_type, int code, const char *msg);
int send_http_internal_error(struct mg_connection *conn);
int send_json_unauthorized(struct mg_connection *conn);
int send_json_error(struct mg_connection *conn, const int code,
int send_http(struct ftl_conn *api, const char *mime_type, const char *msg);
int send_http_code(struct ftl_conn *api, const char *mime_type, int code, const char *msg);
int send_http_internal_error(struct ftl_conn *api);
int send_json_unauthorized(struct ftl_conn *api);
int send_json_error(struct ftl_conn *api, const int code,
const char *key, const char* message,
cJSON *data);
int send_json_success(struct mg_connection *conn);
int send_json_success(struct ftl_conn *api);
void http_reread_index_html(void);
// Cookie routines
bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int *i);
bool http_get_cookie_str(struct mg_connection *conn, const char *cookieName, char *str, size_t str_size);
bool http_get_cookie_int(struct ftl_conn *api, const char *cookieName, int *i);
bool http_get_cookie_str(struct ftl_conn *api, const char *cookieName, char *str, size_t str_size);
// HTTP parameter routines
bool get_bool_var(const char *source, const char *var, bool *boolean);
bool get_uint_var(const char *source, const char *var, unsigned int *num);
bool get_int_var(const char *source, const char *var, int *num);
bool http_get_payload(struct mg_connection *conn, char *payload);
cJSON *get_POST_JSON(struct mg_connection *conn);
// HTTP macros
#define GET_VAR(variable, destination, source) mg_get_var(source, strlen(source), variable, destination, sizeof(destination))
// Method routines
enum http_method { HTTP_UNKNOWN, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_DELETE };
int http_method(struct mg_connection *conn);
// Utils
const char *startsWith(const char *path, const char *uri) __attribute__((pure));
enum http_method __attribute__((pure)) http_method(struct mg_connection *conn);
const char* __attribute__((pure)) startsWith(const char *path, const struct ftl_conn *api);
void read_and_parse_payload(struct ftl_conn *api);
#endif // HTTP_H

View File

@ -30,7 +30,7 @@
if(string_item == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_OBJ_COPY_STR FAILED (key: \"%s\", string: \"%s\")!", key, string); \
return 500; \
} \
@ -50,7 +50,7 @@
if(string_item == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_OBJ_REF_STR FAILED (key: \"%s\", string: \"%s\")!", key, string); \
return 500; \
} \
@ -61,7 +61,7 @@
if(cJSON_AddNumberToObject(object, key, (double)(number)) == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_OBJ_ADD_NUMBER FAILED!"); \
return 500; \
} \
@ -72,7 +72,7 @@
if(null_item == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_OBJ_ADD_NULL FAILED!"); \
return 500; \
} \
@ -84,7 +84,7 @@
if(bool_item == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_OBJ_ADD_BOOL FAILED!"); \
return 500; \
} \
@ -114,7 +114,7 @@
if(string_item == NULL) \
{ \
cJSON_Delete(array); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_ARRAY_REF_STR FAILED!"); \
return 500; \
} \
@ -134,7 +134,7 @@
if(string_item == NULL) \
{ \
cJSON_Delete(array); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_ARRAY_COPY_STR FAILED!"); \
return 500; \
} \
@ -153,11 +153,11 @@
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_SEND_OBJECT FAILED!"); \
return 500; \
} \
send_http(conn, "application/json; charset=utf-8", msg); \
send_http(api, "application/json; charset=utf-8", msg); \
cJSON_Delete(object); \
return 200; \
}
@ -167,11 +167,11 @@
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_SEND_OBJECT_CODE FAILED!"); \
return 500; \
} \
send_http_code(conn, "application/json; charset=utf-8", code, msg); \
send_http_code(api, "application/json; charset=utf-8", code, msg); \
cJSON_Delete(object); \
return code; \
}
@ -181,11 +181,11 @@
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_SEND_OBJECT_AND_HEADERS FAILED!"); \
return 500; \
} \
send_http(conn, "application/json; charset=utf-8", additional_headers, msg); \
send_http(api, "application/json; charset=utf-8", additional_headers, msg); \
cJSON_Delete(object); \
free(additional_headers); \
return 200; \
@ -196,11 +196,11 @@
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_internal_error(conn); \
send_http_internal_error(api); \
logg("JSON_SEND_OBJECT_AND_HEADERS_CODE FAILED!"); \
return 500; \
} \
send_http_code(conn, "application/json; charset=utf-8", additional_headers, code, msg); \
send_http_code(api, "application/json; charset=utf-8", additional_headers, code, msg); \
cJSON_Delete(object); \
free(additional_headers); \
return code; \

View File

@ -22,12 +22,6 @@
// Server context handle
static struct mg_context *ctx = NULL;
// Print passed string directly
static int print_simple(struct mg_connection *conn, void *input)
{
return send_http(conn, "text/plain", input);
}
static int redirect_root_handler(struct mg_connection *conn, void *input)
{
// Get requested host
@ -142,7 +136,7 @@ void http_init(void)
const char *options[] = {
"document_root", httpsettings.webroot,
"listening_ports", httpsettings.port,
"decode_url", "no",
"decode_url", "yes",
"enable_directory_listing", "no",
"num_threads", "16",
"access_control_list", httpsettings.acl,
@ -171,9 +165,6 @@ void http_init(void)
return;
}
/* Add simple demonstration callbacks */
mg_set_request_handler(ctx, "/ping", print_simple, (char*)"pong\n");
// Register API handler
mg_set_request_handler(ctx, "/api", api_handler, NULL);