DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
coaps_server.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file coaps_server.h
6 * @brief CoAP-over-DTLS server front-end - binds a UDP port to a pool of DtlsConn + the CoAPs bridge.
7 *
8 * The socket / per-peer glue on top of coaps_process(): it owns a fixed pool of @ref DtlsConn
9 * handshake engines, binds the CoAPs UDP port (5684, coaps://) through the transport layer (det_udp),
10 * routes each inbound datagram to the connection for its peer address (a new peer opens a pool slot),
11 * drives the DTLS 1.3 handshake and its retransmission timer, and hands established application records
12 * to coap_server_process() through coaps_process(). CoAP resources are registered with the existing
13 * coap_server_add_resource() API; this module only carries them over DTLS, so a plaintext CoAP server
14 * (coap_server_begin_udp on :5683) and this secured one can run side by side over the same resources.
15 *
16 * Threading (ESP32): det_udp delivers datagrams on the lwIP thread, but the handshake engines must run
17 * on the server loop, so the UDP handler only copies each datagram into a lock-free ingest ring;
18 * coaps_server_poll() (called from the loop) drains the ring, runs coaps_process(), fires the PTO
19 * retransmission timer, and reaps idle or failed connections. The engines therefore only ever run in
20 * one context. On host builds there is no UDP; datagrams are injected with coaps_server_ingest() and
21 * replies captured through an output sink, so the whole server is host-testable by shuttling byte
22 * buffers to an in-test DTLS client, exactly like dtls_conn and coaps_process themselves.
23 *
24 * Constrained-friendly: unlike the HTTP/3 pool this is not PSRAM-gated - a small DtlsConn pool fits
25 * internal DRAM, which is the whole point of CoAP. Raise DETWS_COAPS_MAX_CONNS for more simultaneous
26 * peers (each slot is one DtlsConn handshake engine plus its per-connection key material).
27 *
28 * @author Douglas Quigg (dstroy0)
29 * @date 2026
30 */
31
32#ifndef DETERMINISTICESPASYNCWEBSERVER_COAPS_SERVER_H
33#define DETERMINISTICESPASYNCWEBSERVER_COAPS_SERVER_H
34
35#include "ServerConfig.h"
36
37#if DETWS_ENABLE_DTLS && DETWS_ENABLE_COAP
38
39#include <stddef.h>
40#include <stdint.h>
41
42#ifndef DETWS_COAPS_MAX_CONNS
43#define DETWS_COAPS_MAX_CONNS 2 ///< simultaneous CoAPs (DTLS) connections; each slot is one DtlsConn engine
44#endif
45#ifndef DETWS_COAPS_INGEST_RING
46#define DETWS_COAPS_INGEST_RING 6 ///< datagrams buffered from the lwIP thread until coaps_server_poll() drains them
47#endif
48#ifndef DETWS_COAPS_PORT
49#define DETWS_COAPS_PORT 5684 ///< default UDP port the CoAPs server binds (coaps://, RFC 7252 §12.8)
50#endif
51#ifndef DETWS_COAPS_IDLE_MS
52#define DETWS_COAPS_IDLE_MS 60000 ///< reclaim a connection with no inbound datagram for this long (§idle-reaping)
53#endif
54
55/**
56 * @brief The server's long-lived identity plus a randomness source for the DTLS 1.3 handshakes.
57 *
58 * @c cert_der / @c ed25519_seed are the server's certificate and matching signing key. @c cookie_key
59 * is the server-wide secret that keys the HelloRetryRequest cookie MAC (RFC 9147 §5.1). @c rng must be
60 * a CSPRNG; the server calls it per handshake for the X25519 ephemeral private key and the ServerHello
61 * random. The certificate bytes are referenced by pointer and must outlive the server; the seeds are
62 * copied in.
63 */
64struct CoapsServerConfig
65{
66 const uint8_t *cert_der; ///< Ed25519 leaf certificate, DER (referenced by pointer, must outlive the server)
67 size_t cert_len;
68 uint8_t ed25519_seed[32]; ///< 32-byte Ed25519 signing seed (matches @c cert_der)
69 uint8_t cookie_key[32]; ///< 32-byte HelloRetryRequest cookie secret
70 void (*rng)(uint8_t *out, size_t len); ///< CSPRNG: per-handshake X25519 ephemeral + ServerHello random
71};
72
73/**
74 * @brief Start the CoAPs server: install @p cfg, bind @p port over UDP, and route datagrams into the
75 * DtlsConn pool. Register CoAP resources first with coap_server_add_resource().
76 *
77 * @param port UDP port to bind, or 0 for @ref DETWS_COAPS_PORT (5684).
78 * @return false if @p cfg is invalid, or (Arduino) the UDP bind fails; on host builds it always
79 * returns true and is driven through coaps_server_ingest() / the output sink.
80 */
81bool coaps_server_begin(uint16_t port, const CoapsServerConfig *cfg);
82
83/**
84 * @brief Drive the server once: drain queued datagrams into their connections (running the handshake,
85 * or decrypting a CoAP request and answering it), fire the DTLS retransmission timer for any
86 * outstanding flight (RFC 9147 §5.8), and reap closed or idle (@ref DETWS_COAPS_IDLE_MS) connections.
87 * Call every loop iteration. The monotonic clock is @ref detws_millis (no @c now_ms argument).
88 */
89void coaps_server_poll();
90
91/** @brief Number of pool slots currently in use (open connections). For diagnostics / tests. */
92uint8_t coaps_server_active_conns();
93
94/** @brief Stop the server: close the UDP binding and release every pool slot. */
95void coaps_server_stop();
96
97// ---------------------------------------------------------------------------
98// Host / test seam (no UDP on host builds)
99// ---------------------------------------------------------------------------
100#if !defined(ARDUINO)
101/** @brief Sink invoked for every outbound datagram (host builds route sends here instead of UDP). */
102using CoapsServerOutFn = void (*)(void *ctx, const uint8_t *datagram, size_t len, const char *ip, uint16_t port);
103
104/** @brief Register the outbound-datagram sink used on host builds. */
105void coaps_server_set_out_sink(CoapsServerOutFn fn, void *ctx);
106
107/**
108 * @brief Inject a received datagram from @p ip:@p port (the host-build stand-in for the UDP handler).
109 * coaps_server_poll() then processes it exactly as a real datagram. @return false if the ring is full.
110 */
111bool coaps_server_ingest(const uint8_t *datagram, size_t len, const char *ip, uint16_t port);
112#endif
113
114#endif // DETWS_ENABLE_DTLS && DETWS_ENABLE_COAP
115#endif // DETERMINISTICESPASYNCWEBSERVER_COAPS_SERVER_H
User-facing configuration for DeterministicESPAsyncWebServer.