Transfer data

This commit is contained in:
Florian Hoss 2022-07-29 10:27:39 +02:00
parent cac6d98908
commit c43dd6121a
27 changed files with 3237 additions and 0 deletions

285
src/esp32/API.cpp Normal file
View file

@ -0,0 +1,285 @@
#include "API.hpp"
namespace fw
{
API::API(fw::Firewall *firewall, const char *username, const char *password, const String ip, const uint16_t port)
{
this->firewall = firewall;
this->api_ip = ip;
this->api_port = port;
if (this->setup_auth(username, password) == ERROR)
endless_loop();
this->server = new WebServer(port);
this->setup_routing();
this->server->begin();
Serial.printf("%s endpoints -> %s/api\n", TAG, this->get_url_base().c_str());
while (true)
{
this->server->handleClient();
// https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html
sleep(1);
}
}
API::~API()
{
this->server->stop();
}
String API::get_url_base()
{
return "http://" + this->api_ip + ":" + this->api_port;
}
ok_t API::setup_auth(const char *username, const char *password)
{
if (!username || *username == 0x00 || strlen(username) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Username too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.username, username, CREDENTIALS_LENGTH);
if (!password || *password == 0x00 || strlen(password) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Password too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.password, password, CREDENTIALS_LENGTH);
return SUCCESS;
}
auth_t API::check_auth()
{
if (server->authenticate(this->credentials.username, this->credentials.password))
{
return AUTHENTICATED;
}
this->json_message_response("unauthorized", 403);
return DENIED;
}
void API::setup_routing()
{
this->server->on("/api/firewall/rules", HTTP_GET, std::bind(&API::get_firewall_rules_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_GET, std::bind(&API::get_firewall_rule_handler, this));
this->server->on("/api/firewall/rules", HTTP_POST, std::bind(&API::post_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_PUT, std::bind(&API::put_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_DELETE, std::bind(&API::delete_firewall_handler, this));
this->server->on("/api", HTTP_GET, std::bind(&API::get_endpoint_list_handler, this));
this->server->onNotFound(std::bind(&API::not_found_handler, this));
add_endpoint_to_list("/api/firewall/rules", "GET", "Get all Firewall Rules");
add_endpoint_to_list("/api/firewall/rules/<key>", "GET", "Get Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules", "POST", "Create Firewall Rule");
add_endpoint_to_list("/api/firewall/rules/<key>", "PUT", "Update Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules/<key>", "DELETE", "Delete Firewall Rule by key");
}
void API::add_endpoint_to_list(const String uri, const char *method, const char *description)
{
api_endpoint_t *temp;
const String url = get_url_base() + uri;
api_endpoint_t *api_ptr = (api_endpoint_t *)malloc(sizeof(api_endpoint_t));
strncpy(api_ptr->uri, url.c_str(), sizeof(api_ptr->uri));
strncpy(api_ptr->method, method, sizeof(api_ptr->method));
strncpy(api_ptr->description, description, sizeof(api_ptr->description));
if (this->endpoint_head == NULL)
{
this->endpoint_head = api_ptr;
api_ptr->next = NULL;
return;
}
temp = this->endpoint_head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = api_ptr;
api_ptr->next = NULL;
return;
}
void API::not_found_handler()
{
this->json_message_response("see " + get_url_base() + "/api for available routes", 404);
}
void API::get_endpoint_list_handler()
{
this->json_array_response(this->construct_json_api(), 200);
}
void API::get_firewall_rule_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
firewall_rule_t *rule_ptr = firewall->get_rule_from_firewall(rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
void API::get_firewall_rules_handler()
{
if (this->check_auth() == DENIED)
return;
this->json_array_response(this->construct_json_firewall(), 200);
}
void API::post_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->add_rule_to_firewall(args);
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 201);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::put_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->update_rule_of_firewall(args, rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::delete_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (firewall->delete_rule_from_firewall(rule_number) == SUCCESS)
this->json_message_response("firewall rule deleted", 200);
else
this->json_message_response("cannot delete firewall rule", 500);
}
bool API::request_has_all_firewall_parameter()
{
if (!this->server->args())
return false;
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
if (i != KEY && !this->server->hasArg(firewall_fields[i]))
return false;
}
return true;
}
String API::json_new_attribute(String key, String value, bool last)
{
String json_string;
json_string += "\"" + key + "\": \"" + value + "\"";
if (!last)
json_string += ",";
return json_string;
}
String API::json_new_attribute(String key, uint32_t value, bool last)
{
return json_new_attribute(key, String(value), last);
}
void API::json_generic_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, serialized_string);
}
void API::json_array_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, "[" + serialized_string + "]");
}
void API::json_message_response(String message, const uint16_t response_code)
{
String serialized_string = "{";
serialized_string += json_new_attribute("message", message, true);
serialized_string += "}";
this->server->send(response_code, json_response_type, serialized_string);
}
String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute(firewall_fields[KEY], rule_ptr->key);
serialized_string += json_new_attribute(firewall_fields[IP], rule_ptr->ip);
serialized_string += json_new_attribute(firewall_fields[PORT_FROM], rule_ptr->port_from);
serialized_string += json_new_attribute(firewall_fields[PORT_TO], rule_ptr->port_to);
serialized_string += json_new_attribute(firewall_fields[PROTOCOL], protocol_to_string(rule_ptr->protocol));
serialized_string += json_new_attribute(firewall_fields[TARGET], target_to_string(rule_ptr->target), true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_firewall()
{
firewall_rule_t *rule_ptr = firewall->get_rule_head();
String serialized_string;
while (rule_ptr != NULL)
{
serialized_string += construct_json_firewall_rule(rule_ptr);
rule_ptr = rule_ptr->next;
if (rule_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
String API::construct_json_api_endpoint(api_endpoint_t *api_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute("endpoint", api_ptr->uri);
serialized_string += json_new_attribute("description", api_ptr->description);
serialized_string += json_new_attribute("method", api_ptr->method, true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_api()
{
api_endpoint_t *api_ptr = this->endpoint_head;
String serialized_string;
while (api_ptr != NULL)
{
serialized_string += construct_json_api_endpoint(api_ptr);
api_ptr = api_ptr->next;
if (api_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
}

55
src/esp32/API.hpp Normal file
View file

@ -0,0 +1,55 @@
#ifndef ESP32_API_HPP
#define ESP32_API_HPP
#include "WebServer.h"
#include "uri/UriBraces.h"
#include "Firewall.hpp"
#include "Utils.hpp"
namespace fw
{
class API
{
public:
API(Firewall *firewall, const char *username, const char *password, const String ip, const uint16_t port = 8080);
~API();
private:
WebServer *server;
Firewall *firewall;
credential_t credentials;
api_endpoint_t *endpoint_head = NULL;
String api_ip = "0.0.0.0";
uint16_t api_port;
String json_response_type = "application/json; charset=utf-8";
const char *TAG = "[API]";
String get_url_base();
ok_t setup_auth(const char *username, const char *password);
auth_t check_auth();
void setup_routing();
void add_endpoint_to_list(const String uri, const char *method, const char *description);
void not_found_handler();
void get_endpoint_list_handler();
void get_firewall_rule_handler();
void get_firewall_rules_handler();
void post_firewall_handler();
void put_firewall_handler();
void delete_firewall_handler();
bool request_has_all_firewall_parameter();
String json_new_attribute(String key, String value, bool last = false);
String json_new_attribute(String key, uint32_t value, bool last = false);
void json_generic_response(String serialized_string, const uint16_t response_code);
void json_array_response(String serialized_string, const uint16_t response_code);
void json_message_response(String message, const uint16_t response_code);
String construct_json_firewall_rule(firewall_rule_t *rule_ptr);
String construct_json_firewall();
String construct_json_api_endpoint(api_endpoint_t *api_ptr);
String construct_json_api();
};
}
#endif

181
src/esp32/Firewall.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "Firewall.hpp"
namespace fw
{
Firewall::Firewall()
{
this->amount_of_rules = retrieve_amount_of_rules();
for (uint8_t i = 1; i <= this->amount_of_rules; i++)
{
firewall_rule_t *rule_ptr = retrieve_firewall_rule(i);
this->add_rule_to_firewall(rule_ptr, false);
}
}
Firewall::~Firewall()
{
}
firewall_rule_t *Firewall::get_rule_head()
{
return this->rule_head;
}
void Firewall::add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom)
{
store_amount_of_rules(this->amount_of_rules);
if (save_in_eeprom)
Storage::store_firewall_rule(rule_ptr);
if (this->rule_head == NULL)
{
this->rule_head = rule_ptr;
rule_ptr->next = NULL;
return;
}
firewall_rule_t *current_rule;
current_rule = this->rule_head;
while (current_rule->next != NULL)
current_rule = current_rule->next;
current_rule->next = rule_ptr;
rule_ptr->next = NULL;
}
firewall_rule_t *Firewall::add_rule_to_firewall(String *args)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = ++this->amount_of_rules;
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
add_rule_to_firewall(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::update_rule_of_firewall(String *args, const uint8_t key)
{
firewall_rule_t *rule_ptr = get_rule_from_firewall(key);
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
Storage::store_firewall_rule(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::get_rule_from_firewall(const uint8_t key)
{
firewall_rule_t *rule_ptr = this->rule_head;
if (this->rule_head == NULL)
return NULL;
while (rule_ptr->key != key)
{
if (rule_ptr->next == NULL)
return NULL;
else
rule_ptr = rule_ptr->next;
}
return rule_ptr;
}
ok_t Firewall::delete_rule_from_firewall(const uint8_t key)
{
if (this->rule_head == NULL)
return NO_ACTION;
firewall_rule_t *current_rule = this->rule_head;
firewall_rule_t *previous_rule = NULL;
firewall_rule_t *temp = NULL;
while (current_rule->key != key)
{
if (current_rule->next == NULL)
return NO_ACTION;
else
{
previous_rule = current_rule;
current_rule = current_rule->next;
}
}
if (current_rule == this->rule_head)
{
this->rule_head = rule_head->next;
temp = this->rule_head;
}
else
{
previous_rule->next = current_rule->next;
temp = previous_rule->next;
}
while (temp != NULL)
{
temp->key--;
temp = temp->next;
}
free(current_rule);
this->amount_of_rules--;
Storage::store_amount_of_rules(this->amount_of_rules);
if (this->amount_of_rules != 0)
Storage::store_all_firewall_rules(rule_head);
return SUCCESS;
}
my_packet_t *Firewall::get_packet_information(struct pbuf *pbuf)
{
my_packet_t *packet = (my_packet_t *)malloc(sizeof(my_packet_t));
const struct ip_hdr *iphdr = (struct ip_hdr *)pbuf->payload;
u16_t iphdr_hlen = IPH_HL_BYTES(iphdr);
packet->protocol = (firewall_protocol_t)IPH_PROTO(iphdr);
sprintf(packet->ip, "%d.%d.%d.%d", ip4_addr1_16_val(iphdr->src), ip4_addr2_16_val(iphdr->src), ip4_addr3_16_val(iphdr->src), ip4_addr4_16_val(iphdr->src));
if (packet->protocol == PROTOCOL_UDP)
{
const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(udphdr->dest);
}
else if (packet->protocol == PROTOCOL_TCP)
{
const struct tcp_hdr *tcphdr = (const struct tcp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(tcphdr->dest);
}
return packet;
}
bool Firewall::rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet)
{
if (strncmp(rule_ptr->ip, packet->ip, IPV4ADDRESS_LENGTH) == 0)
{
if ((rule_ptr->protocol == PROTOCOL_ALL || packet->protocol == rule_ptr->protocol) &&
is_in_range(packet->port, rule_ptr->port_from, rule_ptr->port_to) &&
rule_ptr->target == TARGET_ACCEPT)
{
free(packet);
return true;
}
}
return false;
}
bool Firewall::is_packet_allowed(struct pbuf *pbuf)
{
// no rules -> no action
if (this->amount_of_rules == 0)
return true;
my_packet_t *packet = get_packet_information(pbuf);
firewall_rule_t *rule_ptr = this->rule_head;
while (rule_ptr != NULL)
{
if (rule_allows_packet(rule_ptr, packet))
return true;
rule_ptr = rule_ptr->next;
}
free(packet);
return false;
}
}

39
src/esp32/Firewall.hpp Normal file
View file

@ -0,0 +1,39 @@
#ifndef ESP32_FIREWALL_HPP
#define ESP32_FIREWALL_HPP
#include "Utils.hpp"
#include "Storage.hpp"
#include "WiFiClient.h"
#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "lwip/ip4.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/prot/tcp.h"
namespace fw
{
class Firewall : public Storage
{
public:
Firewall();
~Firewall();
firewall_rule_t *get_rule_head();
void add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom = true);
firewall_rule_t *add_rule_to_firewall(String *args);
firewall_rule_t *update_rule_of_firewall(String *args, const uint8_t key);
firewall_rule_t *get_rule_from_firewall(const uint8_t key);
ok_t delete_rule_from_firewall(const uint8_t key);
bool is_packet_allowed(struct pbuf *pbuf);
protected:
bool rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet);
my_packet_t *get_packet_information(struct pbuf *pbuf);
uint8_t amount_of_rules = 0;
firewall_rule_t *rule_head = NULL;
};
}
#endif

70
src/esp32/Storage.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "Storage.hpp"
namespace fw
{
Storage::Storage()
{
}
Storage::~Storage()
{
}
uint8_t Storage::retrieve_amount_of_rules()
{
this->memory.begin("settings", true);
const uint8_t amount_of_rules = memory.getUChar("amount_of_rules", 0);
this->memory.end();
return amount_of_rules;
}
void Storage::store_amount_of_rules(const uint8_t new_amount)
{
this->memory.begin("settings", false);
this->memory.putUChar("amount_of_rules", new_amount);
this->memory.end();
}
firewall_rule_t *Storage::retrieve_firewall_rule(const uint8_t key)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = key;
char rulename[10]; // fwRule99\n
sprintf(rulename, "fwRule%i", key);
this->memory.begin(rulename, true);
strncpy(rule_ptr->ip, this->memory.getString(firewall_fields[IP], "0.0.0.0").c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = this->memory.getUShort(firewall_fields[PORT_FROM], 0);
rule_ptr->port_to = this->memory.getUShort(firewall_fields[PORT_TO], 0);
rule_ptr->protocol = static_cast<firewall_protocol_t>(this->memory.getUChar(firewall_fields[PROTOCOL], PROTOCOL_ALL));
rule_ptr->target = static_cast<firewall_target_t>(this->memory.getUChar(firewall_fields[TARGET], TARGET_ACCEPT));
this->memory.end();
return rule_ptr;
}
void Storage::store_all_firewall_rules(firewall_rule_t *rule_head)
{
firewall_rule_t *temp = rule_head;
while (temp != NULL)
{
store_firewall_rule(temp);
temp = temp->next;
}
}
void Storage::store_firewall_rule(firewall_rule_t *rule_ptr)
{
char rulename[10]; // fwRule99\n
sprintf(rulename, "fwRule%i", rule_ptr->key);
this->memory.begin(rulename, false);
this->memory.putString(firewall_fields[IP], rule_ptr->ip);
this->memory.putUShort(firewall_fields[PORT_FROM], rule_ptr->port_from);
this->memory.putUShort(firewall_fields[PORT_TO], rule_ptr->port_to);
this->memory.putUChar(firewall_fields[PROTOCOL], rule_ptr->protocol);
this->memory.putUChar(firewall_fields[TARGET], rule_ptr->target);
this->memory.end();
}
}

27
src/esp32/Storage.hpp Normal file
View file

@ -0,0 +1,27 @@
#ifndef ESP32_STORAGE_HPP
#define ESP32_STORAGE_HPP
#include "Preferences.h"
#include "Utils.hpp"
namespace fw
{
class Storage
{
public:
Storage();
~Storage();
private:
Preferences memory;
protected:
uint8_t retrieve_amount_of_rules();
void store_amount_of_rules(const uint8_t new_amount);
firewall_rule_t *retrieve_firewall_rule(const uint8_t key);
void store_all_firewall_rules(firewall_rule_t *rule_head);
void store_firewall_rule(firewall_rule_t *rule_ptr);
};
}
#endif

58
src/esp32/Utils.cpp Normal file
View file

@ -0,0 +1,58 @@
#include "Utils.hpp"
namespace fw
{
String protocol_to_string(firewall_protocol_t &protocol)
{
switch (protocol)
{
case PROTOCOL_TCP:
return "TCP";
case PROTOCOL_UDP:
return "UDP";
default:
return "ALL";
}
}
firewall_protocol_t string_to_protocol(String &protocol)
{
if (protocol.equals("TCP"))
return PROTOCOL_TCP;
else if (protocol.equals("UDP"))
return PROTOCOL_UDP;
else
return PROTOCOL_ALL;
}
String target_to_string(firewall_target_t &target)
{
switch (target)
{
case TARGET_DROP:
return "DROP";
default:
return "ACCEPT";
}
}
firewall_target_t string_to_target(String &target)
{
if (target.equals("DROP"))
return TARGET_DROP;
else
return TARGET_ACCEPT;
}
void endless_loop()
{
Serial.printf("Something went wrong. Running endless loop until fixed...");
while (true)
delay(500);
}
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper)
{
return lower <= number && number <= upper;
}
}

90
src/esp32/Utils.hpp Normal file
View file

@ -0,0 +1,90 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include "Arduino.h"
#include "WString.h"
namespace fw
{
typedef enum firewall_targets : uint8_t
{
TARGET_DROP = 1,
TARGET_ACCEPT = 2,
} firewall_target_t;
typedef enum firewall_protocols : uint8_t
{
PROTOCOL_TCP = 6,
PROTOCOL_UDP = 17,
PROTOCOL_ALL = 255,
} firewall_protocol_t;
typedef enum ok : uint8_t
{
SUCCESS = 0,
ERROR = 1,
NO_ACTION = 2,
} ok_t;
typedef enum auth : uint8_t
{
AUTHENTICATED = 0,
DENIED = 1,
} auth_t;
static const uint8_t IPV4ADDRESS_LENGTH = 16;
typedef struct firewall_rules
{
uint8_t key;
char ip[IPV4ADDRESS_LENGTH];
uint16_t port_from;
uint16_t port_to;
firewall_protocol_t protocol;
firewall_target_t target;
struct firewall_rules *next;
} firewall_rule_t;
typedef struct my_packet
{
char ip[IPV4ADDRESS_LENGTH];
firewall_protocol_t protocol;
uint16_t port;
} my_packet_t;
static const uint8_t firewall_fields_amount = 6;
const char firewall_fields[firewall_fields_amount][10] = {"key", "ip", "port_from", "port_to", "protocol", "target"};
typedef enum firewall_fields : uint8_t
{
KEY,
IP,
PORT_FROM,
PORT_TO,
PROTOCOL,
TARGET,
} firewall_fields_t;
static const uint8_t CREDENTIALS_LENGTH = 32;
typedef struct credentials
{
char password[CREDENTIALS_LENGTH];
char username[CREDENTIALS_LENGTH];
} credential_t;
typedef struct api_endpoints
{
char uri[60];
char method[7];
char description[30];
struct api_endpoints *next;
} api_endpoint_t;
String protocol_to_string(firewall_protocol_t &protocol);
firewall_protocol_t string_to_protocol(String &protocol);
String target_to_string(firewall_target_t &target);
firewall_target_t string_to_target(String &target);
String response_code_to_string(const uint16_t response_code);
void endless_loop();
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper);
}
#endif

287
src/esp8266/API.cpp Normal file
View file

@ -0,0 +1,287 @@
#include "API.hpp"
namespace fw
{
API::API(fw::Firewall *firewall, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port)
{
this->firewall = firewall;
this->api_ip = ip;
this->api_port = port;
if (this->setup_auth(username, password) == ERROR)
endless_loop();
this->server = new ESP8266WebServerSecure(port);
this->serverCache = new ServerSessions(5);
this->setup_routing(cert, key);
this->server->begin();
Serial.printf("%s endpoints -> %s/api\n", TAG, this->get_url_base().c_str());
}
API::~API()
{
this->server->stop();
}
void API::handle_client()
{
this->server->handleClient();
}
String API::get_url_base()
{
return "https://" + this->api_ip + ":" + this->api_port;
}
ok_t API::setup_auth(const char *username, const char *password)
{
if (!username || *username == 0x00 || strlen(username) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Username too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.username, username, CREDENTIALS_LENGTH);
if (!password || *password == 0x00 || strlen(password) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Password too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.password, password, CREDENTIALS_LENGTH);
return SUCCESS;
}
auth_t API::check_auth()
{
if (server->authenticate(this->credentials.username, this->credentials.password))
{
return AUTHENTICATED;
}
this->json_message_response("unauthorised", 403);
return DENIED;
}
void API::setup_routing(const char *cert, const char *key)
{
this->server->getServer().setRSACert(new BearSSL::X509List(cert), new BearSSL::PrivateKey(key));
this->server->getServer().setCache(serverCache);
this->server->on("/api/firewall/rules", HTTP_GET, std::bind(&API::get_firewall_rules_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_GET, std::bind(&API::get_firewall_rule_handler, this));
this->server->on("/api/firewall/rules", HTTP_POST, std::bind(&API::post_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_PUT, std::bind(&API::put_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_DELETE, std::bind(&API::delete_firewall_handler, this));
this->server->on("/api", HTTP_GET, std::bind(&API::get_endpoint_list_handler, this));
this->server->onNotFound(std::bind(&API::not_found_handler, this));
add_endpoint_to_list("/api/firewall/rules", "GET", "Get all Firewall Rules");
add_endpoint_to_list("/api/firewall/rules/<key>", "GET", "Get Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules", "POST", "Create Firewall Rule");
add_endpoint_to_list("/api/firewall/rules/<key>", "PUT", "Update Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules/<key>", "DELETE", "Delete Firewall Rule by key");
}
void API::add_endpoint_to_list(const String uri, const char *method, const char *description)
{
api_endpoint_t *temp;
const String url = get_url_base() + uri;
api_endpoint_t *api_ptr = (api_endpoint_t *)malloc(sizeof(api_endpoint_t));
strncpy(api_ptr->uri, url.c_str(), sizeof(api_ptr->uri));
strncpy(api_ptr->method, method, sizeof(api_ptr->method));
strncpy(api_ptr->description, description, sizeof(api_ptr->description));
if (this->endpoint_head == NULL)
{
this->endpoint_head = api_ptr;
api_ptr->next = NULL;
return;
}
temp = this->endpoint_head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = api_ptr;
api_ptr->next = NULL;
return;
}
void API::not_found_handler()
{
this->json_message_response("see " + get_url_base() + "/api for available routes", 404);
}
void API::get_endpoint_list_handler()
{
this->json_array_response(this->construct_json_api(), 200);
}
void API::get_firewall_rule_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
firewall_rule_t *rule_ptr = firewall->get_rule_from_firewall(rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
void API::get_firewall_rules_handler()
{
if (this->check_auth() == DENIED)
return;
this->json_array_response(this->construct_json_firewall(), 200);
}
void API::post_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->add_rule_to_firewall(args);
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 201);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::put_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->update_rule_of_firewall(args, rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::delete_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (firewall->delete_rule_from_firewall(rule_number) == SUCCESS)
this->json_message_response("firewall rule deleted", 200);
else
this->json_message_response("cannot delete firewall rule", 500);
}
bool API::request_has_all_firewall_parameter()
{
if (!this->server->args())
return false;
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
if (i != KEY && !this->server->hasArg(firewall_fields[i]))
return false;
}
return true;
}
String API::json_new_attribute(String key, String value, bool last)
{
String json_string;
json_string += "\"" + key + "\": \"" + value + "\"";
if (!last)
json_string += ",";
return json_string;
}
String API::json_new_attribute(String key, uint32_t value, bool last)
{
return json_new_attribute(key, String(value), last);
}
void API::json_generic_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, serialized_string);
}
void API::json_array_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, "[" + serialized_string + "]");
}
void API::json_message_response(String message, const uint16_t response_code)
{
String serialized_string = "{";
serialized_string += json_new_attribute("message", message, true);
serialized_string += "}";
this->server->send(response_code, json_response_type, serialized_string);
}
String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute(firewall_fields[KEY], rule_ptr->key);
serialized_string += json_new_attribute(firewall_fields[IP], rule_ptr->ip);
serialized_string += json_new_attribute(firewall_fields[PORT_FROM], rule_ptr->port_from);
serialized_string += json_new_attribute(firewall_fields[PORT_TO], rule_ptr->port_to);
serialized_string += json_new_attribute(firewall_fields[PROTOCOL], protocol_to_string(rule_ptr->protocol));
serialized_string += json_new_attribute(firewall_fields[TARGET], target_to_string(rule_ptr->target), true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_firewall()
{
firewall_rule_t *rule_ptr = firewall->get_rule_head();
String serialized_string;
while (rule_ptr != NULL)
{
serialized_string += construct_json_firewall_rule(rule_ptr);
rule_ptr = rule_ptr->next;
if (rule_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
String API::construct_json_api_endpoint(api_endpoint_t *api_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute("endpoint", api_ptr->uri);
serialized_string += json_new_attribute("description", api_ptr->description);
serialized_string += json_new_attribute("method", api_ptr->method, true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_api()
{
api_endpoint_t *api_ptr = this->endpoint_head;
String serialized_string;
while (api_ptr != NULL)
{
serialized_string += construct_json_api_endpoint(api_ptr);
api_ptr = api_ptr->next;
if (api_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
}

57
src/esp8266/API.hpp Normal file
View file

@ -0,0 +1,57 @@
#ifndef ESP8266_API_HPP
#define ESP8266_API_HPP
#include "ESP8266WebServerSecure.h"
#include "uri/UriBraces.h"
#include "Firewall.hpp"
#include "Utils.hpp"
namespace fw
{
class API
{
public:
API(Firewall *, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port = 8080);
~API();
void handle_client();
private:
BearSSL::ESP8266WebServerSecure *server;
BearSSL::ServerSessions *serverCache;
Firewall *firewall;
credential_t credentials;
api_endpoint_t *endpoint_head = NULL;
String api_ip = "0.0.0.0";
uint16_t api_port;
String json_response_type = "application/json; charset=utf-8";
const char *TAG = "[API]";
String get_url_base();
ok_t setup_auth(const char *username, const char *password);
auth_t check_auth();
void setup_routing(const char *cert, const char *key);
void add_endpoint_to_list(const String uri, const char *method, const char *description);
void not_found_handler();
void get_endpoint_list_handler();
void get_firewall_rule_handler();
void get_firewall_rules_handler();
void post_firewall_handler();
void put_firewall_handler();
void delete_firewall_handler();
bool request_has_all_firewall_parameter();
String json_new_attribute(String key, String value, bool last = false);
String json_new_attribute(String key, uint32_t value, bool last = false);
void json_generic_response(String serialized_string, const uint16_t response_code);
void json_array_response(String serialized_string, const uint16_t response_code);
void json_message_response(String message, const uint16_t response_code);
String construct_json_firewall_rule(firewall_rule_t *rule_ptr);
String construct_json_firewall();
String construct_json_api_endpoint(api_endpoint_t *api_ptr);
String construct_json_api();
};
}
#endif

181
src/esp8266/Firewall.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "Firewall.hpp"
namespace fw
{
Firewall::Firewall()
{
this->amount_of_rules = retrieve_amount_of_rules();
for (uint8_t i = 1; i <= this->amount_of_rules; i++)
{
firewall_rule_t *rule_ptr = retrieve_firewall_rule(i);
this->add_rule_to_firewall(rule_ptr, false);
}
}
Firewall::~Firewall()
{
}
firewall_rule_t *Firewall::get_rule_head()
{
return this->rule_head;
}
void Firewall::add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom)
{
store_amount_of_rules(this->amount_of_rules);
if (save_in_eeprom)
Storage::store_firewall_rule(rule_ptr);
if (this->rule_head == NULL)
{
this->rule_head = rule_ptr;
rule_ptr->next = NULL;
return;
}
firewall_rule_t *current_rule;
current_rule = this->rule_head;
while (current_rule->next != NULL)
current_rule = current_rule->next;
current_rule->next = rule_ptr;
rule_ptr->next = NULL;
}
firewall_rule_t *Firewall::add_rule_to_firewall(String *args)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = ++this->amount_of_rules;
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
add_rule_to_firewall(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::update_rule_of_firewall(String *args, const uint8_t key)
{
firewall_rule_t *rule_ptr = get_rule_from_firewall(key);
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
Storage::store_firewall_rule(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::get_rule_from_firewall(const uint8_t key)
{
firewall_rule_t *rule_ptr = this->rule_head;
if (this->rule_head == NULL)
return NULL;
while (rule_ptr->key != key)
{
if (rule_ptr->next == NULL)
return NULL;
else
rule_ptr = rule_ptr->next;
}
return rule_ptr;
}
ok_t Firewall::delete_rule_from_firewall(const uint8_t key)
{
if (this->rule_head == NULL)
return NO_ACTION;
firewall_rule_t *current_rule = this->rule_head;
firewall_rule_t *previous_rule = NULL;
firewall_rule_t *temp = NULL;
while (current_rule->key != key)
{
if (current_rule->next == NULL)
return NO_ACTION;
else
{
previous_rule = current_rule;
current_rule = current_rule->next;
}
}
if (current_rule == this->rule_head)
{
this->rule_head = rule_head->next;
temp = this->rule_head;
}
else
{
previous_rule->next = current_rule->next;
temp = previous_rule->next;
}
while (temp != NULL)
{
temp->key--;
temp = temp->next;
}
free(current_rule);
this->amount_of_rules--;
Storage::store_amount_of_rules(this->amount_of_rules);
if (this->amount_of_rules != 0)
Storage::store_all_firewall_rules(rule_head);
return SUCCESS;
}
my_packet_t *Firewall::get_packet_information(struct pbuf *pbuf)
{
my_packet_t *packet = (my_packet_t *)malloc(sizeof(my_packet_t));
const struct ip_hdr *iphdr = (struct ip_hdr *)pbuf->payload;
u16_t iphdr_hlen = IPH_HL_BYTES(iphdr);
packet->protocol = (firewall_protocol_t)IPH_PROTO(iphdr);
sprintf(packet->ip, "%d.%d.%d.%d", ip4_addr1_16_val(iphdr->src), ip4_addr2_16_val(iphdr->src), ip4_addr3_16_val(iphdr->src), ip4_addr4_16_val(iphdr->src));
if (packet->protocol == PROTOCOL_UDP)
{
const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(udphdr->dest);
}
else if (packet->protocol == PROTOCOL_TCP)
{
const struct tcp_hdr *tcphdr = (const struct tcp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(tcphdr->dest);
}
return packet;
}
bool Firewall::rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet)
{
if (strncmp(rule_ptr->ip, packet->ip, IPV4ADDRESS_LENGTH) == 0)
{
if ((rule_ptr->protocol == PROTOCOL_ALL || packet->protocol == rule_ptr->protocol) &&
is_in_range(packet->port, rule_ptr->port_from, rule_ptr->port_to) &&
rule_ptr->target == TARGET_ACCEPT)
{
free(packet);
return true;
}
}
return false;
}
bool Firewall::is_packet_allowed(struct pbuf *pbuf)
{
// no rules -> no action
if (this->amount_of_rules == 0)
return true;
my_packet_t *packet = get_packet_information(pbuf);
firewall_rule_t *rule_ptr = this->rule_head;
while (rule_ptr != NULL)
{
if (rule_allows_packet(rule_ptr, packet))
return true;
rule_ptr = rule_ptr->next;
}
free(packet);
return false;
}
}

38
src/esp8266/Firewall.hpp Normal file
View file

@ -0,0 +1,38 @@
#ifndef ESP8266_FIREWALL_HPP
#define ESP8266_FIREWALL_HPP
#include "Utils.hpp"
#include "Storage.hpp"
#include "WiFiClient.h"
#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "lwip/ip4.h"
#include "lwip/udp.h"
#include "lwip/prot/tcp.h"
namespace fw
{
class Firewall : public Storage
{
public:
Firewall();
~Firewall();
firewall_rule_t *get_rule_head();
void add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom = true);
firewall_rule_t *add_rule_to_firewall(String *args);
firewall_rule_t *update_rule_of_firewall(String *args, const uint8_t key);
firewall_rule_t *get_rule_from_firewall(const uint8_t key);
ok_t delete_rule_from_firewall(const uint8_t key);
bool is_packet_allowed(struct pbuf *pbuf);
protected:
bool rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet);
my_packet_t *get_packet_information(struct pbuf *pbuf);
uint8_t amount_of_rules = 0;
firewall_rule_t *rule_head = NULL;
};
}
#endif

74
src/esp8266/Storage.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "Storage.hpp"
namespace fw
{
Storage::Storage()
{
this->max_rules = 15;
this->eeprom_amount_of_rules = 0;
this->eeprom_rules_head = 1;
this->eeprom_size = this->max_rules * sizeof(firewall_rule_t) + eeprom_rules_head;
EEPROM.begin(this->eeprom_size);
}
Storage::~Storage()
{
}
uint16_t Storage::eeprom_rule_position(uint8_t key)
{
return eeprom_rules_head + (key - 1) * sizeof(firewall_rule_t);
}
uint8_t Storage::retrieve_amount_of_rules()
{
uint8_t amount_of_rules = EEPROM.read(this->eeprom_amount_of_rules);
if (amount_of_rules > this->max_rules)
return 0;
return amount_of_rules;
}
void Storage::store_amount_of_rules(const uint8_t new_amount)
{
EEPROM.put(this->eeprom_amount_of_rules, new_amount);
EEPROM.commit();
}
firewall_rule_t *Storage::retrieve_firewall_rule(const uint8_t key)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = key;
uint16_t eespom_position = eeprom_rule_position(key);
EEPROM.get(eespom_position, rule_ptr->ip);
EEPROM.get(eespom_position += sizeof(rule_ptr->ip), rule_ptr->port_from);
EEPROM.get(eespom_position += sizeof(rule_ptr->port_from), rule_ptr->port_to);
EEPROM.get(eespom_position += sizeof(rule_ptr->port_to), rule_ptr->protocol);
EEPROM.get(eespom_position += sizeof(rule_ptr->protocol), rule_ptr->target);
return rule_ptr;
}
void Storage::store_all_firewall_rules(firewall_rule_t *rule_head)
{
firewall_rule_t *temp = rule_head;
while (temp != NULL)
{
store_firewall_rule(temp);
temp = temp->next;
}
}
void Storage::store_firewall_rule(firewall_rule_t *rule_ptr)
{
uint16_t eespom_position = eeprom_rule_position(rule_ptr->key);
EEPROM.put(eespom_position, rule_ptr->ip);
EEPROM.put(eespom_position += sizeof(rule_ptr->ip), rule_ptr->port_from);
EEPROM.put(eespom_position += sizeof(rule_ptr->port_from), rule_ptr->port_to);
EEPROM.put(eespom_position += sizeof(rule_ptr->port_to), rule_ptr->protocol);
EEPROM.put(eespom_position += sizeof(rule_ptr->protocol), rule_ptr->target);
EEPROM.commit();
}
}

32
src/esp8266/Storage.hpp Normal file
View file

@ -0,0 +1,32 @@
#ifndef ESP8266_STORAGE_HPP
#define ESP8266_STORAGE_HPP
#include "EEPROM.h"
#include "Utils.hpp"
namespace fw
{
class Storage
{
public:
Storage();
~Storage();
private:
uint8_t max_rules;
uint16_t eeprom_size;
uint16_t eeprom_amount_of_rules;
uint16_t eeprom_rules_head;
uint16_t eeprom_rule_position(uint8_t key);
protected:
uint8_t retrieve_amount_of_rules();
void store_amount_of_rules(const uint8_t new_amount);
firewall_rule_t *retrieve_firewall_rule(const uint8_t key);
void store_all_firewall_rules(firewall_rule_t *rule_head);
void store_firewall_rule(firewall_rule_t *rule_ptr);
};
}
#endif

58
src/esp8266/Utils.cpp Normal file
View file

@ -0,0 +1,58 @@
#include "Utils.hpp"
namespace fw
{
String protocol_to_string(firewall_protocol_t &protocol)
{
switch (protocol)
{
case PROTOCOL_TCP:
return "TCP";
case PROTOCOL_UDP:
return "UDP";
default:
return "ALL";
}
}
firewall_protocol_t string_to_protocol(String &protocol)
{
if (protocol.equals("TCP"))
return PROTOCOL_TCP;
else if (protocol.equals("UDP"))
return PROTOCOL_UDP;
else
return PROTOCOL_ALL;
}
String target_to_string(firewall_target_t &target)
{
switch (target)
{
case TARGET_DROP:
return "DROP";
default:
return "ACCEPT";
}
}
firewall_target_t string_to_target(String &target)
{
if (target.equals("DROP"))
return TARGET_DROP;
else
return TARGET_ACCEPT;
}
void endless_loop()
{
Serial.printf("Something went wrong. Running endless loop until fixed...");
while (true)
delay(500);
}
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper)
{
return lower <= number && number <= upper;
}
}

90
src/esp8266/Utils.hpp Normal file
View file

@ -0,0 +1,90 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include "Arduino.h"
#include "WString.h"
namespace fw
{
typedef enum firewall_targets : uint8_t
{
TARGET_DROP = 1,
TARGET_ACCEPT = 2,
} firewall_target_t;
typedef enum firewall_protocols : uint8_t
{
PROTOCOL_TCP = 6,
PROTOCOL_UDP = 17,
PROTOCOL_ALL = 255,
} firewall_protocol_t;
typedef enum ok : uint8_t
{
SUCCESS = 0,
ERROR = 1,
NO_ACTION = 2,
} ok_t;
typedef enum auth : uint8_t
{
AUTHENTICATED = 0,
DENIED = 1,
} auth_t;
static const uint8_t IPV4ADDRESS_LENGTH = 16;
typedef struct firewall_rules
{
uint8_t key;
char ip[IPV4ADDRESS_LENGTH];
uint16_t port_from;
uint16_t port_to;
firewall_protocol_t protocol;
firewall_target_t target;
struct firewall_rules *next;
} firewall_rule_t;
typedef struct my_packet
{
char ip[IPV4ADDRESS_LENGTH];
firewall_protocol_t protocol;
uint16_t port;
} my_packet_t;
static const uint8_t firewall_fields_amount = 6;
const char firewall_fields[firewall_fields_amount][10] = {"key", "ip", "port_from", "port_to", "protocol", "target"};
typedef enum firewall_fields : uint8_t
{
KEY,
IP,
PORT_FROM,
PORT_TO,
PROTOCOL,
TARGET,
} firewall_fields_t;
static const uint8_t CREDENTIALS_LENGTH = 32;
typedef struct credentials
{
char password[CREDENTIALS_LENGTH];
char username[CREDENTIALS_LENGTH];
} credential_t;
typedef struct api_endpoints
{
char uri[60];
char method[7];
char description[30];
struct api_endpoints *next;
} api_endpoint_t;
String protocol_to_string(firewall_protocol_t &protocol);
firewall_protocol_t string_to_protocol(String &protocol);
String target_to_string(firewall_target_t &target);
firewall_target_t string_to_target(String &target);
String response_code_to_string(const uint16_t response_code);
void endless_loop();
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper);
}
#endif