fw: robust HTTP body reads, CORS preflight, LED off at boot; update docs
- app_net.c: replace bare httpd_req_recv with recv_body_capped/alloc helpers (TCP-safe, full-body reads); add OPTIONS/* CORS preflight handler; bump WS broadcast buffer to 2048; add CORS Allow-Methods - CMakeLists (net_service): add http_parser dep for HTTP_OPTIONS - nfc_engine/pn532_core: add nfc_access_lock/unlock mutex, board-RGB quiet helper, UL type detection, general-status improvements - pn532_transport: minor cleanup - main.c: call board_rgb_led_quiet() at boot to kill onboard LED - sdkconfig.defaults: add board RGB Kconfig defaults - README, docs/LIMITATIONS, docs/PINOUT: expand and correct Made-with: Cursor
This commit is contained in:
@@ -2,5 +2,5 @@ idf_component_register(
|
||||
SRCS "app_net.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_http_server http_parser esp_wifi esp_netif nvs_flash mdns esp_timer
|
||||
json spiffs vfs freertos nfc_engine pn532_host
|
||||
json spiffs vfs freertos nfc_engine pn532_host esp_https_ota app_update mbedtls
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "net_service/app_net.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_netif.h"
|
||||
@@ -36,6 +38,7 @@ static httpd_handle_t s_server;
|
||||
static SemaphoreHandle_t s_nfc_op_mu;
|
||||
static volatile bool s_scan = true;
|
||||
static TaskHandle_t s_scan_task;
|
||||
static volatile bool s_target_active;
|
||||
|
||||
static void nfc_access_lock(void)
|
||||
{
|
||||
@@ -105,6 +108,48 @@ static bool copy_json_string(const cJSON *item, char *dst, size_t cap)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bin_to_hex(const uint8_t *src, size_t len, char *dst, size_t dst_cap)
|
||||
{
|
||||
if (!dst || dst_cap == 0) {
|
||||
return;
|
||||
}
|
||||
if (!src || dst_cap < (len * 2 + 1)) {
|
||||
dst[0] = '\0';
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
snprintf(dst + i * 2, 3, "%02X", src[i]);
|
||||
}
|
||||
dst[len * 2] = '\0';
|
||||
}
|
||||
|
||||
static bool add_hex_to_object(cJSON *obj, const char *name, const uint8_t *src, size_t len)
|
||||
{
|
||||
if (!obj || !name || !src) {
|
||||
return false;
|
||||
}
|
||||
char *hex = calloc(1, len * 2 + 1);
|
||||
if (!hex) {
|
||||
return false;
|
||||
}
|
||||
bin_to_hex(src, len, hex, len * 2 + 1);
|
||||
cJSON_AddStringToObject(obj, name, hex);
|
||||
free(hex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int json_get_int_default(const cJSON *obj, const char *name, int fallback)
|
||||
{
|
||||
cJSON *item = cJSON_GetObjectItemCaseSensitive((cJSON *)obj, name);
|
||||
return cJSON_IsNumber(item) ? (int)cJSON_GetNumberValue(item) : fallback;
|
||||
}
|
||||
|
||||
static bool json_get_bool_default(const cJSON *obj, const char *name, bool fallback)
|
||||
{
|
||||
cJSON *item = cJSON_GetObjectItemCaseSensitive((cJSON *)obj, name);
|
||||
return cJSON_IsBool(item) ? cJSON_IsTrue(item) : fallback;
|
||||
}
|
||||
|
||||
/** Read full POST body (httpd may return partial data in multiple recv calls). */
|
||||
static int recv_body_capped(httpd_req_t *req, char *buf, size_t cap)
|
||||
{
|
||||
@@ -177,7 +222,7 @@ esp_err_t app_net_broadcast_json(const char *channel, const char *json_text)
|
||||
httpd_ws_frame_t pkt = {.type = HTTPD_WS_TYPE_TEXT, .payload = (uint8_t *)line, .len = (size_t)n};
|
||||
for (size_t i = 0; i < fdcount; i++) {
|
||||
if (httpd_ws_get_fd_info(s_server, fds[i]) == HTTPD_WS_CLIENT_WEBSOCKET) {
|
||||
(void)httpd_ws_send_frame_async(s_server, fds[i], &pkt);
|
||||
(void)httpd_ws_send_data(s_server, fds[i], &pkt);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -250,6 +295,359 @@ static esp_err_t ensure_mifare_classic_tag_locked(nfc_tag_info_t *tag)
|
||||
return nfc_tag_is_mifare_classic(tag) ? ESP_OK : ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static esp_err_t reject_if_target_active(httpd_req_t *req)
|
||||
{
|
||||
if (!s_target_active) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return send_json(req, cJSON_CreateString("target mode active; stop target mode first"), 409);
|
||||
}
|
||||
|
||||
static cJSON *build_probe_locked(const nfc_tag_info_t *tag)
|
||||
{
|
||||
if (!tag) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *tag_json = nfc_tag_to_json(tag);
|
||||
cJSON *caps = cJSON_CreateObject();
|
||||
if (!root || !tag_json || !caps) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(tag_json);
|
||||
cJSON_Delete(caps);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(root, "tag", tag_json);
|
||||
if (nfc_tag_is_mifare_classic(tag)) {
|
||||
cJSON_AddStringToObject(caps, "family", nfc_tag_is_mifare_classic_4k(tag) ? "mifareClassic4k" : "mifareClassic");
|
||||
cJSON_AddNumberToObject(caps, "sectorCount", nfc_mifare_sector_count(tag));
|
||||
cJSON_AddBoolToObject(caps, "canRead", true);
|
||||
cJSON_AddBoolToObject(caps, "canWrite", true);
|
||||
cJSON_AddBoolToObject(caps, "canDictionaryAttack", true);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneCapture", true);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneProgram", true);
|
||||
cJSON_AddBoolToObject(caps, "manufacturerBlockWritable", false);
|
||||
cJSON_AddBoolToObject(caps, "targetModeRelevant", false);
|
||||
cJSON_AddStringToObject(caps, "note",
|
||||
"Classic clone/program flow can write user blocks and optional trailers, but not manufacturer block 0 on normal cards.");
|
||||
} else if (nfc_tag_is_type2(tag)) {
|
||||
cJSON_AddStringToObject(caps, "family", "type2");
|
||||
cJSON_AddBoolToObject(caps, "canRead", true);
|
||||
cJSON_AddBoolToObject(caps, "canWrite", true);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneCapture", true);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneProgram", true);
|
||||
cJSON_AddNumberToObject(caps, "safeUserPageStart", 4);
|
||||
uint8_t version[8];
|
||||
if (nfc_type2_get_version(version) == ESP_OK) {
|
||||
(void)add_hex_to_object(caps, "getVersionHex", version, sizeof(version));
|
||||
}
|
||||
cJSON_AddStringToObject(caps, "note",
|
||||
"Type 2 clone/program flow writes user pages by default and leaves manufacturer/lock pages untouched unless forced.");
|
||||
} else {
|
||||
cJSON_AddStringToObject(caps, "family", "unknown");
|
||||
cJSON_AddBoolToObject(caps, "canRead", false);
|
||||
cJSON_AddBoolToObject(caps, "canWrite", false);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneCapture", false);
|
||||
cJSON_AddBoolToObject(caps, "structuredCloneProgram", false);
|
||||
cJSON_AddStringToObject(caps, "note",
|
||||
"Use raw commands and manual analysis; this tag does not match the current structured Classic/Type 2 workflows.");
|
||||
}
|
||||
cJSON_AddItemToObject(root, "capabilities", caps);
|
||||
return root;
|
||||
}
|
||||
|
||||
static cJSON *find_sector_hit(const cJSON *attack, int sector)
|
||||
{
|
||||
if (!attack) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *hits = cJSON_GetObjectItemCaseSensitive((cJSON *)attack, "sectorHits");
|
||||
if (!cJSON_IsArray(hits)) {
|
||||
return NULL;
|
||||
}
|
||||
int n = cJSON_GetArraySize(hits);
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON *it = cJSON_GetArrayItem(hits, i);
|
||||
cJSON *s = cJSON_GetObjectItemCaseSensitive(it, "sector");
|
||||
if (cJSON_IsNumber(s) && (int)cJSON_GetNumberValue(s) == sector) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool parse_sector_key_hit(const cJSON *hit, nfc_mifare_key_t *key, char *key_hex_out, size_t key_hex_cap)
|
||||
{
|
||||
if (!hit || !key) {
|
||||
return false;
|
||||
}
|
||||
cJSON *kh = cJSON_GetObjectItemCaseSensitive((cJSON *)hit, "keyHex");
|
||||
cJSON *kt = cJSON_GetObjectItemCaseSensitive((cJSON *)hit, "keyType");
|
||||
if (!cJSON_IsString(kh) || !kh->valuestring || !cJSON_IsString(kt) || !kt->valuestring) {
|
||||
return false;
|
||||
}
|
||||
if (!hex_to_bin(kh->valuestring, key->key, 6)) {
|
||||
return false;
|
||||
}
|
||||
key->key_b = (strcmp(kt->valuestring, "B") == 0);
|
||||
if (key_hex_out && key_hex_cap > 0) {
|
||||
size_t len = strlen(kh->valuestring);
|
||||
if (len >= key_hex_cap) {
|
||||
return false;
|
||||
}
|
||||
memcpy(key_hex_out, kh->valuestring, len + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static cJSON *build_type2_capture_locked(const nfc_tag_info_t *tag, int max_pages)
|
||||
{
|
||||
if (!tag) {
|
||||
return NULL;
|
||||
}
|
||||
if (max_pages < 4) {
|
||||
max_pages = 4;
|
||||
}
|
||||
if (max_pages > 240) {
|
||||
max_pages = 240;
|
||||
}
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *tag_json = nfc_tag_to_json(tag);
|
||||
cJSON *pages = cJSON_CreateArray();
|
||||
if (!root || !tag_json || !pages) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(tag_json);
|
||||
cJSON_Delete(pages);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddStringToObject(root, "format", "type2");
|
||||
cJSON_AddItemToObject(root, "tag", tag_json);
|
||||
uint8_t version[8];
|
||||
if (nfc_type2_get_version(version) == ESP_OK) {
|
||||
(void)add_hex_to_object(root, "getVersionHex", version, sizeof(version));
|
||||
}
|
||||
for (int page = 0; page < max_pages; page++) {
|
||||
uint8_t data[4];
|
||||
if (nfc_ultralight_read_page((uint8_t)page, data) != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
char hx[9];
|
||||
bin_to_hex(data, sizeof(data), hx, sizeof(hx));
|
||||
cJSON_AddItemToArray(pages, cJSON_CreateString(hx));
|
||||
}
|
||||
cJSON_AddNumberToObject(root, "pageCount", cJSON_GetArraySize(pages));
|
||||
cJSON_AddItemToObject(root, "pagesHex", pages);
|
||||
cJSON_AddStringToObject(root, "programNote",
|
||||
"Programming skips pages 0-3 by default because they contain manufacturer and lock/config data.");
|
||||
return root;
|
||||
}
|
||||
|
||||
static cJSON *build_classic_capture_locked(const nfc_tag_info_t *tag, const uint8_t *extra_keys, size_t extra_n,
|
||||
bool variations)
|
||||
{
|
||||
if (!tag) {
|
||||
return NULL;
|
||||
}
|
||||
int sectors = nfc_mifare_sector_count(tag);
|
||||
if (sectors <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *attack = nfc_mifare_dictionary_attack((nfc_tag_info_t *)tag, 0, (uint8_t)(sectors - 1),
|
||||
extra_keys, extra_n, variations, NULL);
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *tag_json = nfc_tag_to_json(tag);
|
||||
cJSON *arr = cJSON_CreateArray();
|
||||
if (!attack || !root || !tag_json || !arr) {
|
||||
cJSON_Delete(attack);
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(tag_json);
|
||||
cJSON_Delete(arr);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddStringToObject(root, "format", nfc_tag_is_mifare_classic_4k(tag) ? "mifareClassic4k" : "mifareClassic");
|
||||
cJSON_AddItemToObject(root, "tag", tag_json);
|
||||
cJSON_AddNumberToObject(root, "sectorCount", sectors);
|
||||
cJSON_AddItemToObject(root, "keySearch", attack);
|
||||
for (int sector = 0; sector < sectors; sector++) {
|
||||
int first_block = 0;
|
||||
int block_count = 0;
|
||||
uint8_t trailer_block = 0;
|
||||
cJSON *sec = cJSON_CreateObject();
|
||||
if (!sec) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(arr);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddNumberToObject(sec, "sector", sector);
|
||||
if (!nfc_mifare_sector_layout(tag, sector, &first_block, &block_count, &trailer_block)) {
|
||||
cJSON_AddBoolToObject(sec, "layoutError", true);
|
||||
cJSON_AddItemToArray(arr, sec);
|
||||
continue;
|
||||
}
|
||||
cJSON_AddNumberToObject(sec, "firstBlock", first_block);
|
||||
cJSON_AddNumberToObject(sec, "blockCount", block_count);
|
||||
cJSON_AddNumberToObject(sec, "trailerBlock", trailer_block);
|
||||
cJSON *hit = find_sector_hit(attack, sector);
|
||||
nfc_mifare_key_t key;
|
||||
char key_hex[13];
|
||||
if (!parse_sector_key_hit(hit, &key, key_hex, sizeof(key_hex))) {
|
||||
cJSON_AddBoolToObject(sec, "authFailed", true);
|
||||
cJSON_AddItemToArray(arr, sec);
|
||||
continue;
|
||||
}
|
||||
cJSON_AddStringToObject(sec, "keyHex", key_hex);
|
||||
cJSON_AddStringToObject(sec, "keyType", key.key_b ? "B" : "A");
|
||||
if (nfc_mifare_authenticate_block(tag, trailer_block, &key) != ESP_OK) {
|
||||
cJSON_AddBoolToObject(sec, "authFailed", true);
|
||||
cJSON_AddItemToArray(arr, sec);
|
||||
continue;
|
||||
}
|
||||
cJSON *blocks = cJSON_CreateArray();
|
||||
if (!blocks) {
|
||||
cJSON_Delete(sec);
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(arr);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < block_count; i++) {
|
||||
uint8_t block_data[NFC_BLOCK_LEN];
|
||||
if (nfc_mifare_read_block((uint8_t)(first_block + i), block_data) == ESP_OK) {
|
||||
char hx[NFC_BLOCK_LEN * 2 + 1];
|
||||
bin_to_hex(block_data, sizeof(block_data), hx, sizeof(hx));
|
||||
cJSON_AddItemToArray(blocks, cJSON_CreateString(hx));
|
||||
} else {
|
||||
cJSON_AddItemToArray(blocks, cJSON_CreateNull());
|
||||
}
|
||||
}
|
||||
cJSON_AddItemToObject(sec, "blocksHex", blocks);
|
||||
cJSON_AddItemToArray(arr, sec);
|
||||
}
|
||||
cJSON_AddItemToObject(root, "sectors", arr);
|
||||
cJSON_AddStringToObject(root, "programNote",
|
||||
"Programming writes data blocks and optionally trailers; block 0 is always skipped on normal Classic cards.");
|
||||
return root;
|
||||
}
|
||||
|
||||
static cJSON *program_type2_snapshot_locked(const cJSON *snapshot, bool include_lock_pages)
|
||||
{
|
||||
cJSON *pages = cJSON_GetObjectItemCaseSensitive((cJSON *)snapshot, "pagesHex");
|
||||
if (!cJSON_IsArray(pages)) {
|
||||
return NULL;
|
||||
}
|
||||
int count = cJSON_GetArraySize(pages);
|
||||
int first_page = include_lock_pages ? 0 : 4;
|
||||
cJSON *out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
int written = 0;
|
||||
int skipped = 0;
|
||||
int failed = 0;
|
||||
for (int page = 0; page < count; page++) {
|
||||
if (page < first_page) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
cJSON *it = cJSON_GetArrayItem(pages, page);
|
||||
if (!cJSON_IsString(it) || !it->valuestring) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
uint8_t data[4];
|
||||
if (!hex_to_bin(it->valuestring, data, sizeof(data))) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
if (nfc_ultralight_write_page((uint8_t)page, data) == ESP_OK) {
|
||||
written++;
|
||||
} else {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
cJSON_AddStringToObject(out, "format", "type2");
|
||||
cJSON_AddNumberToObject(out, "writtenPages", written);
|
||||
cJSON_AddNumberToObject(out, "skippedPages", skipped);
|
||||
cJSON_AddNumberToObject(out, "failedPages", failed);
|
||||
cJSON_AddBoolToObject(out, "includeLockPages", include_lock_pages);
|
||||
return out;
|
||||
}
|
||||
|
||||
static cJSON *program_classic_snapshot_locked(const nfc_tag_info_t *tag, const cJSON *snapshot, bool include_trailers)
|
||||
{
|
||||
if (!tag) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *sectors = cJSON_GetObjectItemCaseSensitive((cJSON *)snapshot, "sectors");
|
||||
if (!cJSON_IsArray(sectors)) {
|
||||
return NULL;
|
||||
}
|
||||
int written = 0;
|
||||
int skipped = 0;
|
||||
int failed = 0;
|
||||
int sectors_n = cJSON_GetArraySize(sectors);
|
||||
for (int si = 0; si < sectors_n; si++) {
|
||||
cJSON *sec = cJSON_GetArrayItem(sectors, si);
|
||||
cJSON *blocks = cJSON_GetObjectItemCaseSensitive(sec, "blocksHex");
|
||||
cJSON *first = cJSON_GetObjectItemCaseSensitive(sec, "firstBlock");
|
||||
cJSON *count = cJSON_GetObjectItemCaseSensitive(sec, "blockCount");
|
||||
cJSON *trailer = cJSON_GetObjectItemCaseSensitive(sec, "trailerBlock");
|
||||
if (!cJSON_IsArray(blocks) || !cJSON_IsNumber(first) || !cJSON_IsNumber(count) || !cJSON_IsNumber(trailer)) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
nfc_mifare_key_t key;
|
||||
if (!parse_sector_key_hit(sec, &key, NULL, 0)) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
uint8_t trailer_block = (uint8_t)cJSON_GetNumberValue(trailer);
|
||||
if (nfc_mifare_authenticate_block(tag, trailer_block, &key) != ESP_OK) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
int first_block = (int)cJSON_GetNumberValue(first);
|
||||
int block_count = (int)cJSON_GetNumberValue(count);
|
||||
int blocks_n = cJSON_GetArraySize(blocks);
|
||||
int limit = block_count < blocks_n ? block_count : blocks_n;
|
||||
for (int bi = 0; bi < limit; bi++) {
|
||||
int block_no = first_block + bi;
|
||||
if (block_no == 0) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
if (block_no == trailer_block && !include_trailers) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
cJSON *it = cJSON_GetArrayItem(blocks, bi);
|
||||
if (!cJSON_IsString(it) || !it->valuestring) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
uint8_t data[NFC_BLOCK_LEN];
|
||||
if (!hex_to_bin(it->valuestring, data, sizeof(data))) {
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
if (nfc_mifare_write_block((uint8_t)block_no, data) == ESP_OK) {
|
||||
written++;
|
||||
} else {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
cJSON *out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddStringToObject(out, "format", "mifareClassic");
|
||||
cJSON_AddNumberToObject(out, "writtenBlocks", written);
|
||||
cJSON_AddNumberToObject(out, "skippedBlocks", skipped);
|
||||
cJSON_AddNumberToObject(out, "failedBlocks", failed);
|
||||
cJSON_AddBoolToObject(out, "includeTrailers", include_trailers);
|
||||
return out;
|
||||
}
|
||||
|
||||
static esp_err_t api_status(httpd_req_t *req)
|
||||
{
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
@@ -274,6 +672,7 @@ static esp_err_t api_status(httpd_req_t *req)
|
||||
}
|
||||
nfc_access_unlock();
|
||||
cJSON_AddBoolToObject(o, "scanning", s_scan);
|
||||
cJSON_AddBoolToObject(o, "targetActive", s_target_active);
|
||||
size_t cap_u = 0;
|
||||
uint32_t cap_l = 0;
|
||||
bool cap_f = false;
|
||||
@@ -365,6 +764,10 @@ static esp_err_t api_session_deep(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_nfc_poll(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char drain[128];
|
||||
(void)recv_body_capped(req, drain, sizeof(drain));
|
||||
|
||||
@@ -411,6 +814,10 @@ static esp_err_t api_scan(httpd_req_t *req)
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
cJSON *en = cJSON_GetObjectItem(j, "enable");
|
||||
if (cJSON_IsBool(en) && cJSON_IsTrue(en) && s_target_active) {
|
||||
cJSON_Delete(j);
|
||||
return send_json(req, cJSON_CreateString("target mode active; stop target mode first"), 409);
|
||||
}
|
||||
if (cJSON_IsBool(en)) {
|
||||
app_net_set_continuous_scan(cJSON_IsTrue(en));
|
||||
}
|
||||
@@ -422,6 +829,10 @@ static esp_err_t api_scan(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_general_status(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
uint8_t gs[32];
|
||||
size_t gl = 0;
|
||||
nfc_access_lock();
|
||||
@@ -445,8 +856,154 @@ static esp_err_t api_general_status(httpd_req_t *req)
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_nfc_probe(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char drain[64];
|
||||
(void)recv_body_capped(req, drain, sizeof(drain));
|
||||
nfc_tag_info_t tag;
|
||||
nfc_access_lock();
|
||||
esp_err_t err = nfc_poll_passive_target(&tag);
|
||||
if (err != ESP_OK) {
|
||||
nfc_access_unlock();
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "error", err == ESP_ERR_NOT_FOUND ? "no tag" : esp_err_to_name(err));
|
||||
return send_json(req, o, err == ESP_ERR_NOT_FOUND ? 400 : 500);
|
||||
}
|
||||
cJSON *o = build_probe_locked(&tag);
|
||||
nfc_access_unlock();
|
||||
if (!o) {
|
||||
return send_json(req, cJSON_CreateString("probe failed"), 500);
|
||||
}
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_clone_capture(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char *body = NULL;
|
||||
cJSON *j = NULL;
|
||||
if (req->content_len > 0) {
|
||||
body = recv_body_alloc(req, 8192, NULL);
|
||||
if (!body) {
|
||||
return send_json(req, cJSON_CreateString("body too large"), 400);
|
||||
}
|
||||
j = cJSON_Parse(body);
|
||||
free(body);
|
||||
if (!j) {
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
} else {
|
||||
j = cJSON_CreateObject();
|
||||
}
|
||||
char mode[16] = "auto";
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "mode"), mode, sizeof(mode));
|
||||
int max_pages = json_get_int_default(j, "maxPages", 64);
|
||||
bool variations = json_get_bool_default(j, "variations", false);
|
||||
uint8_t extra[96 * 6];
|
||||
size_t extra_n = 0;
|
||||
cJSON *keys = cJSON_GetObjectItemCaseSensitive(j, "keysHex");
|
||||
if (cJSON_IsArray(keys)) {
|
||||
int n = cJSON_GetArraySize(keys);
|
||||
for (int i = 0; i < n && extra_n < 96; i++) {
|
||||
cJSON *it = cJSON_GetArrayItem(keys, i);
|
||||
if (cJSON_IsString(it) && it->valuestring && hex_to_bin(it->valuestring, extra + extra_n * 6, 6)) {
|
||||
extra_n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
cJSON_Delete(j);
|
||||
|
||||
nfc_tag_info_t tag;
|
||||
nfc_access_lock();
|
||||
esp_err_t err = nfc_poll_passive_target(&tag);
|
||||
if (err != ESP_OK) {
|
||||
nfc_access_unlock();
|
||||
return send_json(req, cJSON_CreateString("no tag present"), 400);
|
||||
}
|
||||
cJSON *out = NULL;
|
||||
if (strcmp(mode, "classic") == 0) {
|
||||
if (!nfc_tag_is_mifare_classic(&tag)) {
|
||||
nfc_access_unlock();
|
||||
return send_json(req, cJSON_CreateString("tag is not MIFARE Classic"), 400);
|
||||
}
|
||||
out = build_classic_capture_locked(&tag, extra_n ? extra : NULL, extra_n, variations);
|
||||
} else if (strcmp(mode, "type2") == 0) {
|
||||
if (!nfc_tag_is_type2(&tag)) {
|
||||
nfc_access_unlock();
|
||||
return send_json(req, cJSON_CreateString("tag is not Type 2 / Ultralight"), 400);
|
||||
}
|
||||
out = build_type2_capture_locked(&tag, max_pages);
|
||||
} else if (nfc_tag_is_mifare_classic(&tag)) {
|
||||
out = build_classic_capture_locked(&tag, extra_n ? extra : NULL, extra_n, variations);
|
||||
} else if (nfc_tag_is_type2(&tag)) {
|
||||
out = build_type2_capture_locked(&tag, max_pages);
|
||||
}
|
||||
nfc_access_unlock();
|
||||
if (!out) {
|
||||
return send_json(req, cJSON_CreateString("capture unsupported or failed"), 400);
|
||||
}
|
||||
return send_json(req, out, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_clone_program(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char *body = recv_body_alloc(req, 65536, NULL);
|
||||
if (!body) {
|
||||
return send_json(req, cJSON_CreateString("snapshot body too large"), 400);
|
||||
}
|
||||
cJSON *j = cJSON_Parse(body);
|
||||
free(body);
|
||||
if (!j) {
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
cJSON *snapshot = cJSON_GetObjectItemCaseSensitive(j, "snapshot");
|
||||
cJSON *format = snapshot ? cJSON_GetObjectItemCaseSensitive(snapshot, "format") : NULL;
|
||||
bool include_trailers = json_get_bool_default(j, "includeTrailers", false);
|
||||
bool include_lock_pages = json_get_bool_default(j, "includeLockPages", false);
|
||||
if (!cJSON_IsObject(snapshot) || !cJSON_IsString(format) || !format->valuestring) {
|
||||
cJSON_Delete(j);
|
||||
return send_json(req, cJSON_CreateString("snapshot.format required"), 400);
|
||||
}
|
||||
|
||||
nfc_tag_info_t tag;
|
||||
cJSON *out = NULL;
|
||||
nfc_access_lock();
|
||||
if (strcmp(format->valuestring, "type2") == 0) {
|
||||
esp_err_t err = ensure_ultralight_tag_locked(&tag);
|
||||
if (err == ESP_OK) {
|
||||
out = program_type2_snapshot_locked(snapshot, include_lock_pages);
|
||||
}
|
||||
} else if (strncmp(format->valuestring, "mifareClassic", 13) == 0) {
|
||||
esp_err_t err = ensure_mifare_classic_tag_locked(&tag);
|
||||
if (err == ESP_OK) {
|
||||
out = program_classic_snapshot_locked(&tag, snapshot, include_trailers);
|
||||
}
|
||||
}
|
||||
nfc_access_unlock();
|
||||
cJSON_Delete(j);
|
||||
if (!out) {
|
||||
return send_json(req, cJSON_CreateString("program failed or unsupported target"), 400);
|
||||
}
|
||||
return send_json(req, out, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_mifare_read(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[512];
|
||||
char key_hex[13];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
@@ -506,6 +1063,10 @@ static esp_err_t api_mifare_read(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_mifare_write(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[512];
|
||||
char key_hex[13];
|
||||
char data_hex[33];
|
||||
@@ -560,6 +1121,10 @@ static esp_err_t api_mifare_write(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_ul_read(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[128];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
if (r < 0) {
|
||||
@@ -602,6 +1167,10 @@ static esp_err_t api_ul_read(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_ul_write(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[128];
|
||||
char data_hex[9];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
@@ -646,14 +1215,255 @@ static esp_err_t api_ul_write(httpd_req_t *req)
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_ota_stub(httpd_req_t *req)
|
||||
static esp_err_t api_target_status(httpd_req_t *req)
|
||||
{
|
||||
cJSON *o = cJSON_CreateString("Use idf.py app-flash or extend with esp_https_ota + bundle URL");
|
||||
return send_json(req, o, 501);
|
||||
char drain[64];
|
||||
(void)recv_body_capped(req, drain, sizeof(drain));
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(o, "active", s_target_active);
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_target_start(httpd_req_t *req)
|
||||
{
|
||||
char *body = req->content_len ? recv_body_alloc(req, 4096, NULL) : NULL;
|
||||
cJSON *j = body ? cJSON_Parse(body) : cJSON_CreateObject();
|
||||
free(body);
|
||||
if (!j) {
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
char raw_params_hex[600] = {0};
|
||||
char sens_res_hex[5] = "0400";
|
||||
char nfcid1t_hex[7] = "112233";
|
||||
char sel_res_hex[3] = "20";
|
||||
char felica_hex[37] = "01FEA2A3A4A5A6A7C0C1C2C3C4C5C6C7FFFF";
|
||||
char nfcid3t_hex[21] = "F0140102030405060708";
|
||||
char gt_hex[256] = {0};
|
||||
char tk_hex[256] = {0};
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "rawParamsHex"), raw_params_hex, sizeof(raw_params_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "sensResHex"), sens_res_hex, sizeof(sens_res_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "nfcid1tHex"), nfcid1t_hex, sizeof(nfcid1t_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "selResHex"), sel_res_hex, sizeof(sel_res_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "felicaParamsHex"), felica_hex, sizeof(felica_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "nfcid3tHex"), nfcid3t_hex, sizeof(nfcid3t_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "generalTargetHex"), gt_hex, sizeof(gt_hex));
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "historicalTargetHex"), tk_hex, sizeof(tk_hex));
|
||||
int mode_byte = json_get_int_default(j, "modeByte", 0x04);
|
||||
int timeout_ms = json_get_int_default(j, "timeoutMs", 1000);
|
||||
cJSON_Delete(j);
|
||||
|
||||
uint8_t params[256];
|
||||
size_t params_len = 0;
|
||||
if (raw_params_hex[0]) {
|
||||
if (!hex_decode_flex(raw_params_hex, params, sizeof(params), ¶ms_len)) {
|
||||
return send_json(req, cJSON_CreateString("rawParamsHex invalid"), 400);
|
||||
}
|
||||
} else {
|
||||
uint8_t sens_res[2], nfcid1t[3], sel_res[1], felica[18], nfcid3t[10], gt[127], tk[127];
|
||||
size_t gt_len = 0, tk_len = 0;
|
||||
if (!hex_to_bin(sens_res_hex, sens_res, sizeof(sens_res)) ||
|
||||
!hex_to_bin(nfcid1t_hex, nfcid1t, sizeof(nfcid1t)) ||
|
||||
!hex_to_bin(sel_res_hex, sel_res, sizeof(sel_res)) ||
|
||||
!hex_to_bin(felica_hex, felica, sizeof(felica)) ||
|
||||
!hex_to_bin(nfcid3t_hex, nfcid3t, sizeof(nfcid3t)) ||
|
||||
(gt_hex[0] && !hex_decode_flex(gt_hex, gt, sizeof(gt), >_len)) ||
|
||||
(tk_hex[0] && !hex_decode_flex(tk_hex, tk, sizeof(tk), &tk_len))) {
|
||||
return send_json(req, cJSON_CreateString("target parameter hex invalid"), 400);
|
||||
}
|
||||
params[params_len++] = (uint8_t)mode_byte;
|
||||
memcpy(params + params_len, sens_res, sizeof(sens_res));
|
||||
params_len += sizeof(sens_res);
|
||||
memcpy(params + params_len, nfcid1t, sizeof(nfcid1t));
|
||||
params_len += sizeof(nfcid1t);
|
||||
memcpy(params + params_len, sel_res, sizeof(sel_res));
|
||||
params_len += sizeof(sel_res);
|
||||
memcpy(params + params_len, felica, sizeof(felica));
|
||||
params_len += sizeof(felica);
|
||||
memcpy(params + params_len, nfcid3t, sizeof(nfcid3t));
|
||||
params_len += sizeof(nfcid3t);
|
||||
params[params_len++] = (uint8_t)gt_len;
|
||||
if (gt_len) {
|
||||
memcpy(params + params_len, gt, gt_len);
|
||||
params_len += gt_len;
|
||||
}
|
||||
params[params_len++] = (uint8_t)tk_len;
|
||||
if (tk_len) {
|
||||
memcpy(params + params_len, tk, tk_len);
|
||||
params_len += tk_len;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t resp[128];
|
||||
size_t rlen = 0;
|
||||
bool prev_scan = s_scan;
|
||||
s_scan = false;
|
||||
nfc_access_lock();
|
||||
esp_err_t err = pn532_tg_init_as_target(params, params_len, resp, sizeof(resp), &rlen, timeout_ms);
|
||||
nfc_access_unlock();
|
||||
if (err != ESP_OK) {
|
||||
s_scan = prev_scan;
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "error", esp_err_to_name(err));
|
||||
return send_json(req, o, 400);
|
||||
}
|
||||
s_target_active = true;
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(o, "active", true);
|
||||
cJSON_AddNumberToObject(o, "modeByte", mode_byte);
|
||||
(void)add_hex_to_object(o, "responseHex", resp, rlen);
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_target_recv(httpd_req_t *req)
|
||||
{
|
||||
char buf[128];
|
||||
int timeout_ms = 15000;
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
if (r > 0) {
|
||||
cJSON *j = cJSON_Parse(buf);
|
||||
if (j) {
|
||||
timeout_ms = json_get_int_default(j, "timeoutMs", timeout_ms);
|
||||
cJSON_Delete(j);
|
||||
}
|
||||
}
|
||||
if (!s_target_active) {
|
||||
return send_json(req, cJSON_CreateString("target mode not active"), 409);
|
||||
}
|
||||
uint8_t resp[300];
|
||||
size_t rlen = 0;
|
||||
nfc_access_lock();
|
||||
esp_err_t err = pn532_tg_get_data(resp, sizeof(resp), &rlen, timeout_ms);
|
||||
nfc_access_unlock();
|
||||
if (err != ESP_OK) {
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "error", esp_err_to_name(err));
|
||||
return send_json(req, o, 400);
|
||||
}
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
uint8_t status = rlen ? resp[0] : 0xFF;
|
||||
cJSON_AddBoolToObject(o, "ok", status == 0x00);
|
||||
cJSON_AddNumberToObject(o, "status", status);
|
||||
if (rlen > 1) {
|
||||
(void)add_hex_to_object(o, "dataHex", resp + 1, rlen - 1);
|
||||
} else {
|
||||
cJSON_AddStringToObject(o, "dataHex", "");
|
||||
}
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_target_send(httpd_req_t *req)
|
||||
{
|
||||
char buf[1024];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
if (r < 0) {
|
||||
return send_json(req, cJSON_CreateString("body required"), 400);
|
||||
}
|
||||
cJSON *j = cJSON_Parse(buf);
|
||||
if (!j) {
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
char data_hex[521] = {0};
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "dataHex"), data_hex, sizeof(data_hex));
|
||||
int timeout_ms = json_get_int_default(j, "timeoutMs", 1000);
|
||||
cJSON_Delete(j);
|
||||
if (!s_target_active) {
|
||||
return send_json(req, cJSON_CreateString("target mode not active"), 409);
|
||||
}
|
||||
uint8_t data[260];
|
||||
size_t data_len = 0;
|
||||
if (!data_hex[0] || !hex_decode_flex(data_hex, data, sizeof(data), &data_len)) {
|
||||
return send_json(req, cJSON_CreateString("dataHex required"), 400);
|
||||
}
|
||||
uint8_t resp[64];
|
||||
size_t rlen = 0;
|
||||
nfc_access_lock();
|
||||
esp_err_t err = pn532_tg_set_data(data, data_len, resp, sizeof(resp), &rlen, timeout_ms);
|
||||
nfc_access_unlock();
|
||||
if (err != ESP_OK) {
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "error", esp_err_to_name(err));
|
||||
return send_json(req, o, 400);
|
||||
}
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
uint8_t status = rlen ? resp[0] : 0xFF;
|
||||
cJSON_AddBoolToObject(o, "ok", status == 0x00);
|
||||
cJSON_AddNumberToObject(o, "status", status);
|
||||
(void)add_hex_to_object(o, "responseHex", resp, rlen);
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_target_stop(httpd_req_t *req)
|
||||
{
|
||||
char drain[64];
|
||||
(void)recv_body_capped(req, drain, sizeof(drain));
|
||||
nfc_access_lock();
|
||||
esp_err_t err = pn532_sam_config_normal();
|
||||
nfc_access_unlock();
|
||||
s_target_active = false;
|
||||
s_scan = true;
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(o, "active", false);
|
||||
if (err != ESP_OK) {
|
||||
cJSON_AddStringToObject(o, "error", esp_err_to_name(err));
|
||||
return send_json(req, o, 400);
|
||||
}
|
||||
return send_json(req, o, 200);
|
||||
}
|
||||
|
||||
static esp_err_t api_ota_real(httpd_req_t *req)
|
||||
{
|
||||
char buf[1024];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
if (r < 0) {
|
||||
return send_json(req, cJSON_CreateString("url required"), 400);
|
||||
}
|
||||
cJSON *j = cJSON_Parse(buf);
|
||||
if (!j) {
|
||||
return send_json(req, cJSON_CreateString("bad json"), 400);
|
||||
}
|
||||
char url[512] = {0};
|
||||
(void)copy_json_string(cJSON_GetObjectItemCaseSensitive(j, "url"), url, sizeof(url));
|
||||
bool reboot = json_get_bool_default(j, "reboot", true);
|
||||
cJSON_Delete(j);
|
||||
if (!url[0] || strncmp(url, "https://", 8) != 0) {
|
||||
return send_json(req, cJSON_CreateString("https url required"), 400);
|
||||
}
|
||||
|
||||
bool prev_scan = s_scan;
|
||||
s_scan = false;
|
||||
esp_http_client_config_t http_cfg = {
|
||||
.url = url,
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
.keep_alive_enable = true,
|
||||
.timeout_ms = 120000,
|
||||
};
|
||||
esp_https_ota_config_t ota_cfg = {
|
||||
.http_config = &http_cfg,
|
||||
};
|
||||
esp_err_t err = esp_https_ota(&ota_cfg);
|
||||
if (err != ESP_OK) {
|
||||
s_scan = prev_scan;
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(o, "error", esp_err_to_name(err));
|
||||
return send_json(req, o, 500);
|
||||
}
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(o, "ok", true);
|
||||
cJSON_AddBoolToObject(o, "rebooting", reboot);
|
||||
esp_err_t send_err = send_json(req, o, 200);
|
||||
if (send_err == ESP_OK && reboot) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_restart();
|
||||
}
|
||||
return send_err;
|
||||
}
|
||||
|
||||
static esp_err_t api_mifare_dictionary_attack(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char *body = recv_body_alloc(req, 8192, NULL);
|
||||
if (!body) {
|
||||
return send_json(req, cJSON_CreateString("no body or too large"), 400);
|
||||
@@ -711,6 +1521,10 @@ static esp_err_t api_mifare_dictionary_attack(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_nfc_emulate_raw(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[1024];
|
||||
char hex[521];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
@@ -754,6 +1568,10 @@ static esp_err_t api_nfc_emulate_raw(httpd_req_t *req)
|
||||
|
||||
static esp_err_t api_raw_pn532(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t gate = reject_if_target_active(req);
|
||||
if (gate != ESP_OK) {
|
||||
return gate;
|
||||
}
|
||||
char buf[1024];
|
||||
char hex[521];
|
||||
int r = recv_body_capped(req, buf, sizeof(buf));
|
||||
@@ -1026,11 +1844,41 @@ static httpd_handle_t start_server(void)
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/probe", .method = HTTP_POST, .handler = api_nfc_probe};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/scan", .method = HTTP_POST, .handler = api_scan};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/target/status", .method = HTTP_POST, .handler = api_target_status};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/target/start", .method = HTTP_POST, .handler = api_target_start};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/target/recv", .method = HTTP_POST, .handler = api_target_recv};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/target/send", .method = HTTP_POST, .handler = api_target_send};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/nfc/target/stop", .method = HTTP_POST, .handler = api_target_stop};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/pn532/general-status", .method = HTTP_GET, .handler = api_general_status};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
@@ -1061,7 +1909,17 @@ static httpd_handle_t start_server(void)
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/ota", .method = HTTP_POST, .handler = api_ota_stub};
|
||||
u = (httpd_uri_t){.uri = "/api/clone/capture", .method = HTTP_POST, .handler = api_clone_capture};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/clone/program", .method = HTTP_POST, .handler = api_clone_program};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
}
|
||||
u = (httpd_uri_t){.uri = "/api/ota", .method = HTTP_POST, .handler = api_ota_real};
|
||||
if (!register_uri_checked(s, &u)) {
|
||||
httpd_stop(s);
|
||||
return NULL;
|
||||
@@ -1187,6 +2045,8 @@ esp_err_t app_net_init(void)
|
||||
}
|
||||
|
||||
if (xTaskCreate(scan_loop_task, "nfc_scan", 20480, NULL, 5, &s_scan_task) != pdPASS) {
|
||||
httpd_stop(s_server);
|
||||
s_server = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
Reference in New Issue
Block a user