/* SPDX-License-Identifier: GPL-3.0-only */ /* Authenticated, bounded WebSocket transport for the serial session broker. */ #pragma once #include #include #include #include "esp_err.h" #include "esp_http_server.h" #include "session_broker.h" #include "user_database.h" #include "web_session_store.h" #ifdef __cplusplus extern "C" { #endif #define WEB_SERIAL_TRANSPORT_MAX_SESSIONS 2U #define WEB_SERIAL_TRANSPORT_MAX_TICKETS 4U #define WEB_SERIAL_TRANSPORT_TICKET_LENGTH 32U #define WEB_SERIAL_TRANSPORT_TICKET_CAPACITY \ (WEB_SERIAL_TRANSPORT_TICKET_LENGTH + 1U) #define WEB_SERIAL_TRANSPORT_TICKET_LIFETIME_SECONDS 30U #define WEB_SERIAL_TRANSPORT_MAX_RX_PAYLOAD 1024U #define WEB_SERIAL_TRANSPORT_TX_PAYLOAD_SIZE 512U #define WEB_SERIAL_TRANSPORT_TICKET_URI "/api/ws-ticket" #define WEB_SERIAL_TRANSPORT_WS_URI "/ws/serial" #define WEB_SERIAL_TRANSPORT_TICKET_QUERY_KEY "ticket" typedef struct { uint64_t tickets_issued; uint64_t tickets_consumed; uint64_t tickets_rejected; uint64_t tickets_expired; uint64_t connections; uint64_t connection_failures; uint64_t disconnections; uint64_t service_start_failures; uint64_t broker_failures; uint64_t writer_requests; uint64_t writer_grants; uint64_t writer_denials; uint64_t writer_releases; uint64_t writer_revocations; uint64_t rx_ws_frames_accepted; uint64_t rx_ws_frames_rejected; uint64_t rx_ws_bytes_accepted; uint64_t rx_ws_bytes_rejected; uint64_t tx_binary_frames; uint64_t tx_binary_bytes; uint64_t tx_control_frames; uint64_t tx_control_bytes; uint64_t send_failures; uint64_t queue_failures; uint64_t protocol_errors; uint64_t close_requests; } web_serial_transport_counters_t; typedef struct { bool active; bool principal_valid; bool writer; bool tx_pending; bool close_requested; int socket_fd; uint32_t generation; session_broker_client_id_t broker_client_id; user_role_t user_role; user_auth_method_t auth_method; char username[USER_DATABASE_USERNAME_CAPACITY + 1U]; } web_serial_transport_session_snapshot_t; typedef struct { bool initialized; bool server_attached; uint32_t active_sessions; uint32_t active_tickets; web_serial_transport_session_snapshot_t sessions[WEB_SERIAL_TRANSPORT_MAX_SESSIONS]; web_serial_transport_counters_t counters; } web_serial_transport_snapshot_t; /* * Allocate no per-session heap objects and start the permanent transport task. * CONFIG_HTTPD_WS_SUPPORT must be enabled. CONFIG_HTTPD_QUEUE_WORK_BLOCKING must * be disabled because that IDF mode can wait forever inside httpd_queue_work(). */ esp_err_t web_serial_transport_init(void); /* Attach after httpd start; detach as part of stopping that same server. */ esp_err_t web_serial_transport_attach_server(httpd_handle_t server); esp_err_t web_serial_transport_detach_server(httpd_handle_t server); /* * Mint a one-time bearer ticket bound to a current authenticated principal and * originating web-session ID (zero only for the shipped Basic path). The * principal is copied; the output is exactly 32 Base64URL characters plus a * terminator and expires after 30 monotonic seconds. Never log or persist it. */ esp_err_t web_serial_transport_mint_ticket(const user_principal_t *principal, web_session_id_t web_session_id, char *ticket, size_t capacity); /* * Convenience POST response helper for /api/ws-ticket. Authentication is * intentionally outside this module: pass the principal returned by successful * authentication, and its session ID (zero for Basic). Cookie callers must also * enforce CSRF/Origin policy. Register as HTTP_POST, not as a public handler. */ esp_err_t web_serial_transport_handle_authenticated_ticket_request( httpd_req_t *request, const user_principal_t *principal, web_session_id_t web_session_id); /* * Handler for /ws/serial. Register as HTTP_GET with is_websocket=true and * handle_ws_control_frames=false. The initial upgraded GET authenticates the * ticket; later invocations process one complete data frame. */ esp_err_t web_serial_transport_ws_handler(httpd_req_t *request); /* Trusted future cookie-authorized upgrade caller; must validate cookie/Origin * first. Zero identifies only the shipped Basic path, never a cookie fallback. */ esp_err_t web_serial_transport_session_ws_handler(httpd_req_t *request, web_session_id_t web_session_id); /* Invalidates the store first, then marks only matching tickets/slots for owner * cleanup. Safe to repeat after either store or transport slot reuse. */ esp_err_t web_serial_transport_revoke_web_session(web_session_id_t id); esp_err_t web_serial_transport_get_snapshot( web_serial_transport_snapshot_t *snapshot); /* Clearing counters does not alter tickets, sessions, ownership, or queued data. */ esp_err_t web_serial_transport_clear_counters(void); /* Invalidate cookie records and tickets/sockets for one username (also after * deletion), or all accounts. Store invalidation occurs even if serial init * failed; these do not touch the HTTPD-owned Basic cache, which rechecks DB. */ esp_err_t web_serial_transport_revoke_user(const uint8_t *username, size_t username_length); esp_err_t web_serial_transport_revoke_sessions(void); #ifdef __cplusplus } #endif