Transfer data
This commit is contained in:
parent
cac6d98908
commit
c43dd6121a
27 changed files with 3237 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Florian Hoss
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
4
esp32example/.gitignore
vendored
Normal file
4
esp32example/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.pio
|
||||||
|
.vscode
|
||||||
|
include/theSecrets.h
|
||||||
|
components
|
3
esp32example/CMakeLists.txt
Normal file
3
esp32example/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(ESP32Firewall)
|
16
esp32example/include/lwip_hooks.h
Normal file
16
esp32example/include/lwip_hooks.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef _LWIP_HOOKS_H_
|
||||||
|
#define _LWIP_HOOKS_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int lwip_hook_ip4_input(struct pbuf *pbuf, struct netif *input_netif);
|
||||||
|
#define LWIP_HOOK_IP4_INPUT lwip_hook_ip4_input
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LWIP_HOOKS_H_ */
|
9
esp32example/include/theSecrets-example.h
Normal file
9
esp32example/include/theSecrets-example.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _THE_SECRETS_H_
|
||||||
|
#define _THE_SECRETS_H_
|
||||||
|
|
||||||
|
const char *ssid = "Wifi";
|
||||||
|
const char *psk = "password";
|
||||||
|
const char *username = "username";
|
||||||
|
const char *password = "password";
|
||||||
|
|
||||||
|
#endif
|
23
esp32example/platformio.ini
Normal file
23
esp32example/platformio.ini
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
default_envs = esp32
|
||||||
|
|
||||||
|
[env:esp32]
|
||||||
|
platform = espressif32
|
||||||
|
board = az-delivery-devkit-v4
|
||||||
|
framework = espidf
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_compat_mode = off
|
||||||
|
build_flags =
|
||||||
|
'-Iinclude'
|
||||||
|
'-DESP_IDF_LWIP_HOOK_FILENAME="lwip_hooks.h"'
|
||||||
|
lib_deps = https://gitlab.hs-esslingen.de/toheer/iot-security-tools.git
|
1439
esp32example/sdkconfig.esp32
Normal file
1439
esp32example/sdkconfig.esp32
Normal file
File diff suppressed because it is too large
Load diff
6
esp32example/src/CMakeLists.txt
Normal file
6
esp32example/src/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# This file was automatically generated for projects
|
||||||
|
# without default 'CMakeLists.txt' file.
|
||||||
|
|
||||||
|
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${app_sources})
|
72
esp32example/src/main.cpp
Normal file
72
esp32example/src/main.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "theSecrets.h"
|
||||||
|
#include "WiFi.h"
|
||||||
|
#include "lwip_hooks.h"
|
||||||
|
|
||||||
|
#include "esp32/Firewall.hpp"
|
||||||
|
#include "esp32/API.hpp"
|
||||||
|
|
||||||
|
fw::Firewall *firewall;
|
||||||
|
fw::API *firewallApi;
|
||||||
|
|
||||||
|
int lwip_hook_ip4_input(struct pbuf *pbuf, struct netif *input_netif)
|
||||||
|
{
|
||||||
|
// Firewall is not setup yet
|
||||||
|
if (firewall != NULL)
|
||||||
|
{
|
||||||
|
if (firewall->is_packet_allowed(pbuf))
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pbuf_free(pbuf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initFirewall(const String ip)
|
||||||
|
{
|
||||||
|
firewall = new fw::Firewall();
|
||||||
|
firewallApi = new fw::API(firewall, username, password, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_wifi_events(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
|
{
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_START:
|
||||||
|
Serial.println("[WiFi] connecting...");
|
||||||
|
break;
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||||
|
Serial.println("[WiFi] connected");
|
||||||
|
break;
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
|
Serial.println("[WiFi] disconnected");
|
||||||
|
WiFi.reconnect();
|
||||||
|
break;
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||||
|
initFirewall(WiFi.localIP().toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.print("[WiFi] other event: ");
|
||||||
|
Serial.println(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.onEvent(handle_wifi_events, ARDUINO_EVENT_MAX);
|
||||||
|
WiFi.begin(ssid, psk);
|
||||||
|
|
||||||
|
// fix for https://github.com/espressif/arduino-esp32/issues/4732
|
||||||
|
WiFi.config(((u32_t)0x0UL), ((u32_t)0x0UL), ((u32_t)0x0UL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html
|
||||||
|
sleep(1);
|
||||||
|
}
|
20
library.json
Normal file
20
library.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "esp_firewall_api",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Florian Hoss",
|
||||||
|
"email": "flhoit00@hs-esslingen.de",
|
||||||
|
"maintainer": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keywords": "communication, esp32, esp8266, firewall",
|
||||||
|
"description": "An Arduino library for editing and creating firewall rules via REST API on an ESP32/ESP8266",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitlab.hs-esslingen.de/toheer/iot-security-tools.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": ["espressif32", "espressif8266"]
|
||||||
|
}
|
285
src/esp32/API.cpp
Normal file
285
src/esp32/API.cpp
Normal 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
55
src/esp32/API.hpp
Normal 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
181
src/esp32/Firewall.cpp
Normal 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
39
src/esp32/Firewall.hpp
Normal 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
70
src/esp32/Storage.cpp
Normal 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
27
src/esp32/Storage.hpp
Normal 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
58
src/esp32/Utils.cpp
Normal 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
90
src/esp32/Utils.hpp
Normal 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
287
src/esp8266/API.cpp
Normal 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
57
src/esp8266/API.hpp
Normal 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
181
src/esp8266/Firewall.cpp
Normal 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
38
src/esp8266/Firewall.hpp
Normal 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
74
src/esp8266/Storage.cpp
Normal 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
32
src/esp8266/Storage.hpp
Normal 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
58
src/esp8266/Utils.cpp
Normal 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
90
src/esp8266/Utils.hpp
Normal 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
|
Reference in a new issue