upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/tollgate_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/tollgate_api.c')
-rw-r--r--main/tollgate_api.c69
1 files changed, 55 insertions, 14 deletions
diff --git a/main/tollgate_api.c b/main/tollgate_api.c
index 2af04bc..efb5cdf 100644
--- a/main/tollgate_api.c
+++ b/main/tollgate_api.c
@@ -2,10 +2,12 @@
2#include "cashu.h" 2#include "cashu.h"
3#include "config.h" 3#include "config.h"
4#include "session.h" 4#include "session.h"
5#include "firewall.h"
5#include "esp_log.h" 6#include "esp_log.h"
6#include "cJSON.h" 7#include "cJSON.h"
7#include "lwip/sockets.h" 8#include "lwip/sockets.h"
8#include "lwip/netdb.h" 9#include "lwip/netdb.h"
10#include "freertos/task.h"
9#include <string.h> 11#include <string.h>
10 12
11static const char *TAG = "tollgate_api"; 13static const char *TAG = "tollgate_api";
@@ -179,11 +181,22 @@ static esp_err_t api_post_payment(httpd_req_t *req)
179 181
180 ESP_LOGI(TAG, "Payment received: %d bytes", total); 182 ESP_LOGI(TAG, "Payment received: %d bytes", total);
181 183
182 cashu_token_t token; 184 cashu_token_t *token = malloc(sizeof(cashu_token_t));
183 esp_err_t err = cashu_decode_token(body, &token); 185 if (!token) {
186 cJSON *notice = create_notice("error", "session-error", "Out of memory");
187 char *json = cJSON_PrintUnformatted(notice);
188 httpd_resp_set_status(req, "503 Service Unavailable");
189 httpd_resp_set_type(req, "application/json");
190 httpd_resp_send(req, json, strlen(json));
191 cJSON_free(json);
192 cJSON_Delete(notice);
193 return ESP_OK;
194 }
195 esp_err_t err = cashu_decode_token(body, token);
184 free(body); 196 free(body);
185 197
186 if (err != ESP_OK) { 198 if (err != ESP_OK) {
199 free(token);
187 cJSON *notice = create_notice("error", "payment-error-invalid", "Failed to decode Cashu token"); 200 cJSON *notice = create_notice("error", "payment-error-invalid", "Failed to decode Cashu token");
188 char *json = cJSON_PrintUnformatted(notice); 201 char *json = cJSON_PrintUnformatted(notice);
189 httpd_resp_set_status(req, "400 Bad Request"); 202 httpd_resp_set_status(req, "400 Bad Request");
@@ -194,8 +207,9 @@ static esp_err_t api_post_payment(httpd_req_t *req)
194 return ESP_OK; 207 return ESP_OK;
195 } 208 }
196 209
197 const char *mint_url = token.mint_url[0] ? token.mint_url : tollgate_config_get()->mint_url; 210 const char *mint_url = token->mint_url[0] ? token->mint_url : tollgate_config_get()->mint_url;
198 if (!cashu_is_mint_accepted(mint_url)) { 211 if (!cashu_is_mint_accepted(mint_url)) {
212 free(token);
199 cJSON *notice = create_notice("error", "payment-error-mint-not-accepted", "Mint not accepted"); 213 cJSON *notice = create_notice("error", "payment-error-mint-not-accepted", "Mint not accepted");
200 char *json = cJSON_PrintUnformatted(notice); 214 char *json = cJSON_PrintUnformatted(notice);
201 httpd_resp_set_status(req, "402 Payment Required"); 215 httpd_resp_set_status(req, "402 Payment Required");
@@ -206,8 +220,9 @@ static esp_err_t api_post_payment(httpd_req_t *req)
206 return ESP_OK; 220 return ESP_OK;
207 } 221 }
208 222
209 for (int i = 0; i < token.proof_count; i++) { 223 for (int i = 0; i < token->proof_count; i++) {
210 if (session_is_secret_spent(token.proofs[i].secret)) { 224 if (session_is_secret_spent(token->proofs[i].secret)) {
225 free(token);
211 cJSON *notice = create_notice("error", "payment-error-token-spent", "Token already spent"); 226 cJSON *notice = create_notice("error", "payment-error-token-spent", "Token already spent");
212 char *json = cJSON_PrintUnformatted(notice); 227 char *json = cJSON_PrintUnformatted(notice);
213 httpd_resp_set_status(req, "402 Payment Required"); 228 httpd_resp_set_status(req, "402 Payment Required");
@@ -219,10 +234,24 @@ static esp_err_t api_post_payment(httpd_req_t *req)
219 } 234 }
220 } 235 }
221 236
222 cashu_proof_state_t states[CASHU_MAX_PROOFS]; 237 cashu_proof_state_t *states = malloc(CASHU_MAX_PROOFS * sizeof(cashu_proof_state_t));
238 if (!states) {
239 free(token);
240 cJSON *notice = create_notice("error", "session-error", "Out of memory");
241 char *json = cJSON_PrintUnformatted(notice);
242 httpd_resp_set_status(req, "503 Service Unavailable");
243 httpd_resp_set_type(req, "application/json");
244 httpd_resp_send(req, json, strlen(json));
245 cJSON_free(json);
246 cJSON_Delete(notice);
247 return ESP_OK;
248 }
223 int state_count = 0; 249 int state_count = 0;
224 err = cashu_check_proof_states(mint_url, &token, states, &state_count); 250 err = cashu_check_proof_states(mint_url, token, states, &state_count);
251 ESP_LOGI(TAG, "Stack HWM after checkstate: %u", uxTaskGetStackHighWaterMark(NULL));
225 if (err != ESP_OK) { 252 if (err != ESP_OK) {
253 free(states);
254 free(token);
226 cJSON *notice = create_notice("error", "payment-error-verification", "Failed to verify token with mint"); 255 cJSON *notice = create_notice("error", "payment-error-verification", "Failed to verify token with mint");
227 char *json = cJSON_PrintUnformatted(notice); 256 char *json = cJSON_PrintUnformatted(notice);
228 httpd_resp_set_status(req, "502 Bad Gateway"); 257 httpd_resp_set_status(req, "502 Bad Gateway");
@@ -235,6 +264,8 @@ static esp_err_t api_post_payment(httpd_req_t *req)
235 264
236 for (int i = 0; i < state_count; i++) { 265 for (int i = 0; i < state_count; i++) {
237 if (states[i].spent) { 266 if (states[i].spent) {
267 free(states);
268 free(token);
238 cJSON *notice = create_notice("error", "payment-error-token-spent", "Token already spent"); 269 cJSON *notice = create_notice("error", "payment-error-token-spent", "Token already spent");
239 char *json = cJSON_PrintUnformatted(notice); 270 char *json = cJSON_PrintUnformatted(notice);
240 httpd_resp_set_status(req, "402 Payment Required"); 271 httpd_resp_set_status(req, "402 Payment Required");
@@ -247,8 +278,10 @@ static esp_err_t api_post_payment(httpd_req_t *req)
247 } 278 }
248 279
249 const tollgate_config_t *cfg = tollgate_config_get(); 280 const tollgate_config_t *cfg = tollgate_config_get();
250 uint64_t allotment = cashu_calculate_allotment_ms(token.total_amount, cfg->price_per_step, cfg->step_size_ms); 281 uint64_t allotment = cashu_calculate_allotment_ms(token->total_amount, cfg->price_per_step, cfg->step_size_ms);
251 if (allotment == 0) { 282 if (allotment == 0) {
283 free(states);
284 free(token);
252 cJSON *notice = create_notice("error", "payment-error-insufficient", "Token value too low"); 285 cJSON *notice = create_notice("error", "payment-error-insufficient", "Token value too low");
253 char *json = cJSON_PrintUnformatted(notice); 286 char *json = cJSON_PrintUnformatted(notice);
254 httpd_resp_set_status(req, "402 Payment Required"); 287 httpd_resp_set_status(req, "402 Payment Required");
@@ -259,11 +292,14 @@ static esp_err_t api_post_payment(httpd_req_t *req)
259 return ESP_OK; 292 return ESP_OK;
260 } 293 }
261 294
295 int secret_count = token->proof_count > 5 ? 5 : token->proof_count;
262 const char *secrets[5]; 296 const char *secrets[5];
263 for (int i = 0; i < token.proof_count && i < 5; i++) { 297 for (int i = 0; i < secret_count; i++) {
264 secrets[i] = token.proofs[i].secret; 298 secrets[i] = token->proofs[i].secret;
265 } 299 }
266 session_t *session = session_create(client_ip, allotment, secrets, token.proof_count); 300 session_t *session = session_create(client_ip, allotment, secrets, secret_count);
301 free(states);
302 free(token);
267 if (!session) { 303 if (!session) {
268 cJSON *notice = create_notice("error", "session-error", "Failed to create session"); 304 cJSON *notice = create_notice("error", "session-error", "Failed to create session");
269 char *json = cJSON_PrintUnformatted(notice); 305 char *json = cJSON_PrintUnformatted(notice);
@@ -310,12 +346,17 @@ static esp_err_t api_get_usage(httpd_req_t *req)
310static esp_err_t api_get_whoami(httpd_req_t *req) 346static esp_err_t api_get_whoami(httpd_req_t *req)
311{ 347{
312 uint32_t client_ip = 0; 348 uint32_t client_ip = 0;
313 char resp[64]; 349 char resp[96];
314 if (get_client_ip(req, &client_ip) == ESP_OK) { 350 if (get_client_ip(req, &client_ip) == ESP_OK) {
351 char mac[18] = {0};
315 esp_ip4_addr_t ip = { .addr = client_ip }; 352 esp_ip4_addr_t ip = { .addr = client_ip };
316 snprintf(resp, sizeof(resp), "mac=" IPSTR, IP2STR(&ip)); 353 if (firewall_get_mac_for_ip(client_ip, mac, sizeof(mac)) == ESP_OK) {
354 snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=%s", IP2STR(&ip), mac);
355 } else {
356 snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=unknown", IP2STR(&ip));
357 }
317 } else { 358 } else {
318 snprintf(resp, sizeof(resp), "mac=unknown"); 359 snprintf(resp, sizeof(resp), "ip=unknown mac=unknown");
319 } 360 }
320 httpd_resp_set_type(req, "text/plain"); 361 httpd_resp_set_type(req, "text/plain");
321 httpd_resp_send(req, resp, strlen(resp)); 362 httpd_resp_send(req, resp, strlen(resp));