11#if DETWS_ENABLE_DTLS && DETWS_ENABLE_COAP
28#ifndef DETWS_COAPS_MAX_DATAGRAM
29#define DETWS_COAPS_MAX_DATAGRAM 1500
33constexpr size_t DETWS_COAPS_OUT_CAP = 2048;
35constexpr size_t DETWS_COAPS_PEER_SER = 6;
40 uint8_t data[DETWS_COAPS_MAX_DATAGRAM];
63struct CoapsServerPoolCtx
65 CoapsSlot pool[DETWS_COAPS_MAX_CONNS];
66 CoapsIngest ring[DETWS_COAPS_INGEST_RING];
68CoapsServerPoolCtx s_cpool;
77 const uint8_t *cert_der =
nullptr;
79 uint8_t ed25519_seed[32];
80 uint8_t cookie_key[32];
81 void (*rng)(uint8_t *out,
size_t len) =
nullptr;
85 CoapsServerOutFn out_sink =
nullptr;
86 void *out_ctx =
nullptr;
89CoapsServerCtx s_coaps;
91void copy_str(
char *dst,
size_t cap,
const char *src)
95 while (src[n] && n + 1 < cap)
106bool serialize_peer(
const char *ip, uint16_t port, uint8_t out[DETWS_COAPS_PEER_SER])
112 for (
const char *p = ip;; p++)
114 if (*p >=
'0' && *p <=
'9')
116 oct = oct * 10 + (uint32_t)(*p -
'0');
118 if (oct > 255 || ndig > 3)
121 else if (*p ==
'.' || *p == 0)
125 out[idx++] = (uint8_t)oct;
137 out[4] = (uint8_t)(port >> 8);
138 out[5] = (uint8_t)(port & 0xFF);
142void server_send(
const char *ip, uint16_t port,
const uint8_t *data,
size_t len)
147 if (s_coaps.out_sink)
148 s_coaps.out_sink(s_coaps.out_ctx, data, len, ip, port);
153bool ring_push(
const uint8_t *dg,
size_t len,
const char *ip, uint16_t port)
155 if (len == 0 || len > DETWS_COAPS_MAX_DATAGRAM)
157 size_t head = s_coaps.ring_head;
158 size_t next = (head + 1) % DETWS_COAPS_INGEST_RING;
159 if (next == (
size_t)s_coaps.ring_tail)
161 CoapsIngest *e = &s_cpool.ring[head];
162 memcpy(e->data, dg, len);
163 e->len = (uint16_t)len;
164 copy_str(e->ip,
sizeof e->ip, ip);
166 s_coaps.ring_head = next;
170bool ring_pop(CoapsIngest *out)
172 size_t tail = s_coaps.ring_tail;
173 if (tail == (
size_t)s_coaps.ring_head)
175 *out = s_cpool.ring[tail];
176 s_coaps.ring_tail = (tail + 1) % DETWS_COAPS_INGEST_RING;
181CoapsSlot *slot_by_peer(
const char *ip, uint16_t port)
183 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
185 CoapsSlot *s = &s_cpool.pool[i];
186 if (s->used && s->peer_port == port && strcmp(s->peer_ip, ip) == 0)
195CoapsSlot *slot_by_cid(
const uint8_t *cid,
size_t avail)
197 uint8_t sc[DTLS_CID_MAX];
198 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
200 CoapsSlot *s = &s_cpool.pool[i];
203 size_t sl = dtls_conn_local_cid(&s->conn, sc);
204 if (sl && sl <= avail && memcmp(cid, sc, sl) == 0)
210CoapsSlot *alloc_slot()
212 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
213 if (!s_cpool.pool[i].used)
215 CoapsSlot *s = &s_cpool.pool[i];
216 memset(s, 0,
sizeof *s);
224CoapsSlot *open_conn(
const char *ip, uint16_t port)
226 CoapsSlot *s = alloc_slot();
229 s_coaps.rng(s->eph,
sizeof s->eph);
230 s_coaps.rng(s->srand,
sizeof s->srand);
231 s->cfg.cert_der = s_coaps.cert_der;
232 s->cfg.cert_len = s_coaps.cert_len;
233 s->cfg.ed25519_seed = s_coaps.ed25519_seed;
234 s->cfg.ephemeral_priv = s->eph;
235 s->cfg.server_random = s->srand;
236 s->cfg.cookie_key = s_coaps.cookie_key;
237 uint8_t paddr[DETWS_COAPS_PEER_SER];
238 bool ok = serialize_peer(ip, port, paddr);
239 dtls_conn_init(&s->conn, &s->cfg, ok ? paddr : nullptr, ok ? sizeof paddr : 0);
240 copy_str(s->peer_ip,
sizeof s->peer_ip, ip);
246void udp_ingest_cb(
const uint8_t *data,
size_t len,
DetUdpPeer *peer,
void * )
252 ring_push(data, len, ip, port);
258void coaps_route_datagram(
const CoapsIngest *ig, uint32_t now, uint8_t *out,
size_t out_cap)
263 bool cid_rec = ig->len >= 1 && (ig->data[0] & 0xE0) == 0x20 && (ig->data[0] & 0x10);
264 CoapsSlot *s = cid_rec ? slot_by_cid(ig->data + 1, ig->len - 1) : nullptr;
266 s = slot_by_peer(ig->ip, ig->port);
268 s = open_conn(ig->ip, ig->port);
272 if (cid_rec && (s->peer_port != ig->port || strcmp(s->peer_ip, ig->ip) != 0))
274 copy_str(s->peer_ip,
sizeof s->peer_ip, ig->ip);
275 s->peer_port = ig->port;
278 int n = coaps_process(&s->conn, ig->data, ig->len, out, out_cap);
280 server_send(s->peer_ip, s->peer_port, out, (
size_t)n);
287void coaps_service_slot(CoapsSlot *s, uint32_t now, uint8_t *out,
size_t out_cap)
291 if (dtls_conn_timeout_ms(&s->conn) == 0)
293 int n = dtls_conn_on_timeout(&s->conn, out, out_cap);
295 server_send(s->peer_ip, s->peer_port, out, (
size_t)n);
302 if (now - s->last_ms >= DETWS_COAPS_IDLE_MS)
307bool coaps_server_begin(uint16_t port,
const CoapsServerConfig *cfg)
309 if (!cfg || !cfg->rng || !cfg->cert_der || cfg->cert_len == 0)
311 s_coaps.cert_der = cfg->cert_der;
312 s_coaps.cert_len = cfg->cert_len;
313 memcpy(s_coaps.ed25519_seed, cfg->ed25519_seed,
sizeof s_coaps.ed25519_seed);
314 memcpy(s_coaps.cookie_key, cfg->cookie_key,
sizeof s_coaps.cookie_key);
315 s_coaps.rng = cfg->rng;
316 s_coaps.port = port ? port : DETWS_COAPS_PORT;
317 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
318 s_cpool.pool[i].used =
false;
319 s_coaps.ring_head = 0;
320 s_coaps.ring_tail = 0;
321 s_coaps.running =
true;
329void coaps_server_poll()
331 if (!s_coaps.running)
334 uint8_t out[DETWS_COAPS_OUT_CAP];
339 while (ring_pop(&ig))
340 coaps_route_datagram(&ig, now, out,
sizeof out);
343 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
344 coaps_service_slot(&s_cpool.pool[i], now, out,
sizeof out);
347uint8_t coaps_server_active_conns()
350 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
351 if (s_cpool.pool[i].used)
356void coaps_server_stop()
358 s_coaps.running =
false;
359 for (uint8_t i = 0; i < DETWS_COAPS_MAX_CONNS; i++)
360 s_cpool.pool[i].used =
false;
361 s_coaps.ring_head = 0;
362 s_coaps.ring_tail = 0;
366void coaps_server_set_out_sink(CoapsServerOutFn fn,
void *ctx)
368 s_coaps.out_sink = fn;
369 s_coaps.out_ctx = ctx;
372bool coaps_server_ingest(
const uint8_t *datagram,
size_t len,
const char *ip, uint16_t port)
374 return ring_push(datagram, len, ip, port);
Pluggable monotonic clock for all library timing.
uint32_t detws_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
CoAP over DTLS (CoAPs, RFC 7252 §9) - the bridge between the DTLS 1.3 server and the CoAP request han...
CoAP-over-DTLS server front-end - binds a UDP port to a pool of DtlsConn + the CoAPs bridge.
DTLS 1.3 server handshake state machine (RFC 9147 §5-6).
Shared single-producer / single-consumer byte-ring primitive.
bool det_udp_peer_addr(const struct DetUdpPeer *peer, char *ip_out, size_t ip_cap, uint16_t *port_out)
Copy a received peer's source IPv4 address (dotted-quad) and port out.
bool det_udp_listen(uint16_t port, DetUdpHandler handler, void *ctx)
Bind a UDP port and route incoming datagrams to handler.
bool det_udp_listener_sendto(uint16_t listen_port, const char *dst_ip, uint16_t dst_port, const uint8_t *data, size_t len)
Send a datagram from the listener bound on listen_port to an address.
Layer 4 (Transport) - connectionless UDP datagram service.