ESP32/ESP8266 Firewall 1.0.0
a software firewall for ESP23 or ESP8266
API.cpp
1#include "API.hpp"
2
3namespace fw
4{
5 API::API(fw::Firewall *firewall, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port)
6 {
7 this->firewall = firewall;
8 this->api_ip = ip;
9 this->api_port = port;
10 if (this->setup_auth(username, password) == ERROR)
11 endless_loop();
12#ifdef ESP8266
13 this->server = new ESP8266WebServerSecure(port);
14 this->serverCache = new ServerSessions(5);
15#else
16 this->server = new WebServer(port);
17#endif
18 this->setup_routing(cert, key);
19 this->server->begin();
20 Serial.printf("%s endpoints -> %s/api\n", TAG, this->get_url_base().c_str());
21#if !defined(ESP8266)
22 while (true)
23 {
24 this->server->handleClient();
25 // https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html
26 sleep(1);
27 }
28#endif
29 }
30
32 {
33 this->server->stop();
34 }
35
36#ifdef ESP8266
37 void API::handle_client()
38 {
39 this->server->handleClient();
40 }
41#endif
42
43 String API::get_url_base()
44 {
45#ifdef ESP8266
46 return "https://" + this->api_ip + ":" + this->api_port;
47#else
48 return "http://" + this->api_ip + ":" + this->api_port;
49#endif
50 }
51
52 ok_t API::setup_auth(const char *username, const char *password)
53 {
54 if (!username || *username == 0x00 || strlen(username) > CREDENTIALS_LENGTH)
55 {
56 Serial.printf("%s Username too long or missing!\n", TAG);
57 return ERROR;
58 }
59 strncpy(credentials.username, username, CREDENTIALS_LENGTH);
60 if (!password || *password == 0x00 || strlen(password) > CREDENTIALS_LENGTH)
61 {
62 Serial.printf("%s Password too long or missing!\n", TAG);
63 return ERROR;
64 }
65 strncpy(credentials.password, password, CREDENTIALS_LENGTH);
66 return SUCCESS;
67 }
68
69 auth_t API::check_auth()
70 {
71 if (server->authenticate(this->credentials.username, this->credentials.password))
72 {
73 return AUTHENTICATED;
74 }
75 this->json_message_response("unauthorized", 403);
76 return DENIED;
77 }
78
79 void API::setup_routing(const char *cert, const char *key)
80 {
81#ifdef ESP8266
82 this->server->getServer().setRSACert(new BearSSL::X509List(cert), new BearSSL::PrivateKey(key));
83 this->server->getServer().setCache(serverCache);
84#endif
85 this->server->on("/api/firewall/rules", HTTP_GET, std::bind(&API::get_firewall_rules_handler, this));
86 this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_GET, std::bind(&API::get_firewall_rule_handler, this));
87 this->server->on("/api/firewall/rules", HTTP_POST, std::bind(&API::post_firewall_handler, this));
88 this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_PUT, std::bind(&API::put_firewall_handler, this));
89 this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_DELETE, std::bind(&API::delete_firewall_handler, this));
90 this->server->on("/api", HTTP_GET, std::bind(&API::get_endpoint_list_handler, this));
91 this->server->onNotFound(std::bind(&API::not_found_handler, this));
92
93 add_endpoint_to_list("/api/firewall/rules", "GET", "Get all Firewall Rules");
94 add_endpoint_to_list("/api/firewall/rules/<key>", "GET", "Get Firewall Rule by key");
95 add_endpoint_to_list("/api/firewall/rules", "POST", "Create Firewall Rule");
96 add_endpoint_to_list("/api/firewall/rules/<key>", "PUT", "Update Firewall Rule by key");
97 add_endpoint_to_list("/api/firewall/rules/<key>", "DELETE", "Delete Firewall Rule by key");
98 }
99
100 void API::add_endpoint_to_list(const String uri, const char *method, const char *description)
101 {
102 api_endpoint_t *temp;
103 const String url = get_url_base() + uri;
104
105 api_endpoint_t *api_ptr = (api_endpoint_t *)malloc(sizeof(api_endpoint_t));
106 strncpy(api_ptr->uri, url.c_str(), sizeof(api_ptr->uri));
107 strncpy(api_ptr->method, method, sizeof(api_ptr->method));
108 strncpy(api_ptr->description, description, sizeof(api_ptr->description));
109
110 if (this->endpoint_head == NULL)
111 {
112 this->endpoint_head = api_ptr;
113 api_ptr->next = NULL;
114 return;
115 }
116 temp = this->endpoint_head;
117 while (temp->next != NULL)
118 {
119 temp = temp->next;
120 }
121 temp->next = api_ptr;
122 api_ptr->next = NULL;
123 return;
124 }
125
126 void API::not_found_handler()
127 {
128 this->json_message_response("see " + get_url_base() + "/api for available routes", 404);
129 }
130
131 void API::get_endpoint_list_handler()
132 {
133 this->json_array_response(this->construct_json_api(), 200);
134 }
135
136 void API::get_firewall_rule_handler()
137 {
138 if (this->check_auth() == DENIED)
139 return;
140 String param = this->server->pathArg(0);
141 int rule_number = atoi(param.c_str());
142 firewall_rule_t *rule_ptr = firewall->get_rule_from_firewall(rule_number);
143 if (rule_ptr == NULL)
144 this->json_message_response("rule does not exist", 404);
145 else
146 this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
147 }
148
149 void API::get_firewall_rules_handler()
150 {
151 if (this->check_auth() == DENIED)
152 return;
153 this->json_array_response(this->construct_json_firewall(), 200);
154 }
155
156 void API::post_firewall_handler()
157 {
158 if (this->check_auth() == DENIED)
159 return;
160 if (request_has_all_firewall_parameter())
161 {
162 String args[IPV4ADDRESS_LENGTH] = {};
163 for (uint8_t i = 0; i < firewall_fields_amount; i++)
164 {
165 args[i] = this->server->arg(firewall_fields[i]);
166 }
167 firewall_rule_t *rule_ptr = firewall->add_rule_to_firewall(args);
168 this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 201);
169 }
170 else
171 {
172 this->json_message_response("not enough parameter provided", 400);
173 }
174 }
175
176 void API::put_firewall_handler()
177 {
178 if (this->check_auth() == DENIED)
179 return;
180 String param = this->server->pathArg(0);
181 int rule_number = atoi(param.c_str());
182 if (request_has_all_firewall_parameter())
183 {
184 String args[IPV4ADDRESS_LENGTH] = {};
185 for (uint8_t i = 0; i < firewall_fields_amount; i++)
186 {
187 args[i] = this->server->arg(firewall_fields[i]);
188 }
189 firewall_rule_t *rule_ptr = firewall->update_rule_of_firewall(args, rule_number);
190 if (rule_ptr == NULL)
191 this->json_message_response("rule does not exist", 404);
192 else
193 this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
194 }
195 else
196 {
197 this->json_message_response("not enough parameter provided", 400);
198 }
199 }
200
201 void API::delete_firewall_handler()
202 {
203 if (this->check_auth() == DENIED)
204 return;
205 String param = this->server->pathArg(0);
206 int rule_number = atoi(param.c_str());
207 if (firewall->delete_rule_from_firewall(rule_number) == SUCCESS)
208 this->json_message_response("firewall rule deleted", 200);
209 else
210 this->json_message_response("cannot delete firewall rule", 500);
211 }
212
213 bool API::request_has_all_firewall_parameter()
214 {
215 if (!this->server->args())
216 return false;
217 for (uint8_t i = 0; i < firewall_fields_amount; i++)
218 {
219 if (i != KEY && !this->server->hasArg(firewall_fields[i]))
220 return false;
221 }
222 return true;
223 }
224
225 String API::json_new_attribute(String key, String value, bool last)
226 {
227 String json_string;
228 json_string += "\"" + key + "\": \"" + value + "\"";
229 if (!last)
230 json_string += ",";
231 return json_string;
232 }
233
234 String API::json_new_attribute(String key, uint32_t value, bool last)
235 {
236 return json_new_attribute(key, String(value), last);
237 }
238
239 void API::json_generic_response(String serialized_string, const uint16_t response_code)
240 {
241 this->server->send(response_code, json_response_type, serialized_string);
242 }
243
244 void API::json_array_response(String serialized_string, const uint16_t response_code)
245 {
246 this->server->send(response_code, json_response_type, "[" + serialized_string + "]");
247 }
248
249 void API::json_message_response(String message, const uint16_t response_code)
250 {
251 String serialized_string = "{";
252 serialized_string += json_new_attribute("message", message, true);
253 serialized_string += "}";
254 this->server->send(response_code, json_response_type, serialized_string);
255 }
256
257 String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
258 {
259 String serialized_string = "{";
260 serialized_string += json_new_attribute(firewall_fields[KEY], rule_ptr->key);
261 serialized_string += json_new_attribute(firewall_fields[IP], rule_ptr->ip);
262 serialized_string += json_new_attribute(firewall_fields[PORT_FROM], rule_ptr->port_from);
263 serialized_string += json_new_attribute(firewall_fields[PORT_TO], rule_ptr->port_to);
264 serialized_string += json_new_attribute(firewall_fields[PROTOCOL], protocol_to_string(rule_ptr->protocol));
265 serialized_string += json_new_attribute(firewall_fields[TARGET], target_to_string(rule_ptr->target), true);
266 serialized_string += "}";
267 return serialized_string;
268 }
269
270 String API::construct_json_firewall()
271 {
272 firewall_rule_t *rule_ptr = firewall->get_rule_head();
273 String serialized_string;
274 while (rule_ptr != NULL)
275 {
276 serialized_string += construct_json_firewall_rule(rule_ptr);
277 rule_ptr = rule_ptr->next;
278 if (rule_ptr != NULL)
279 serialized_string += ",";
280 }
281 return serialized_string;
282 }
283
284 String API::construct_json_api_endpoint(api_endpoint_t *api_ptr)
285 {
286 String serialized_string = "{";
287 serialized_string += json_new_attribute("endpoint", api_ptr->uri);
288 serialized_string += json_new_attribute("description", api_ptr->description);
289 serialized_string += json_new_attribute("method", api_ptr->method, true);
290 serialized_string += "}";
291 return serialized_string;
292 }
293
294 String API::construct_json_api()
295 {
296 api_endpoint_t *api_ptr = this->endpoint_head;
297 String serialized_string;
298 while (api_ptr != NULL)
299 {
300 serialized_string += construct_json_api_endpoint(api_ptr);
301 api_ptr = api_ptr->next;
302 if (api_ptr != NULL)
303 serialized_string += ",";
304 }
305 return serialized_string;
306 }
307}
API(Firewall *, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port=8080)
Construct a new API object.
Definition: API.cpp:5
~API()
Destroy the API object.
Definition: API.cpp:31