11#if PC_ENABLE_DTLS && PC_ENABLE_COAP
28#ifndef PC_COAPS_MAX_DATAGRAM
29#define PC_COAPS_MAX_DATAGRAM 1500
33#define PC_COAPS_OUT_CAP 2048
35#define PC_COAPS_PEER_SER 6
40 uint8_t data[PC_COAPS_MAX_DATAGRAM];
63struct CoapsServerPoolCtx
65 CoapsSlot pool[PC_COAPS_MAX_CONNS];
66 CoapsIngest ring[PC_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)
96 while (src[n] && n + 1 < cap)
108bool serialize_peer(
const char *ip, uint16_t port, uint8_t out[PC_COAPS_PEER_SER])
114 for (
const char *p = ip;; p++)
116 if (*p >=
'0' && *p <=
'9')
118 oct = oct * 10 + (uint32_t)(*p -
'0');
120 if (oct > 255 || ndig > 3)
125 else if (*p ==
'.' || *p == 0)
131 out[idx++] = (uint8_t)oct;
149 out[4] = (uint8_t)(port >> 8);
150 out[5] = (uint8_t)(port & 0xFF);
154void server_send(
const char *ip, uint16_t port,
const uint8_t *data,
size_t len)
159 if (s_coaps.out_sink)
161 s_coaps.out_sink(s_coaps.out_ctx, data, len, ip, port);
167bool ring_push(
const uint8_t *dg,
size_t len,
const char *ip, uint16_t port)
169 if (len == 0 || len > PC_COAPS_MAX_DATAGRAM)
173 size_t head = s_coaps.ring_head;
174 size_t next = (head + 1) % PC_COAPS_INGEST_RING;
175 if (next == (
size_t)s_coaps.ring_tail)
179 CoapsIngest *e = &s_cpool.ring[head];
180 memcpy(e->data, dg, len);
181 e->len = (uint16_t)len;
182 copy_str(e->ip,
sizeof e->ip, ip);
184 s_coaps.ring_head = next;
188bool ring_pop(CoapsIngest *out)
190 size_t tail = s_coaps.ring_tail;
191 if (tail == (
size_t)s_coaps.ring_head)
195 *out = s_cpool.ring[tail];
196 s_coaps.ring_tail = (tail + 1) % PC_COAPS_INGEST_RING;
201CoapsSlot *slot_by_peer(
const char *ip, uint16_t port)
203 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
205 CoapsSlot *s = &s_cpool.pool[i];
206 if (s->used && s->peer_port == port && strcmp(s->peer_ip, ip) == 0)
217CoapsSlot *slot_by_cid(
const uint8_t *cid,
size_t avail)
219 uint8_t sc[PC_DTLS_CID_MAX];
220 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
222 CoapsSlot *s = &s_cpool.pool[i];
227 size_t sl = pc_dtls_conn_local_cid(&s->conn, sc);
228 if (sl && sl <= avail && memcmp(cid, sc, sl) == 0)
236CoapsSlot *alloc_slot()
238 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
240 if (!s_cpool.pool[i].used)
242 CoapsSlot *s = &s_cpool.pool[i];
243 memset(s, 0,
sizeof *s);
252CoapsSlot *open_conn(
const char *ip, uint16_t port)
254 CoapsSlot *s = alloc_slot();
259 s_coaps.rng(s->eph,
sizeof s->eph);
260 s_coaps.rng(s->srand,
sizeof s->srand);
261 s->cfg.cert_der = s_coaps.cert_der;
262 s->cfg.cert_len = s_coaps.cert_len;
263 s->cfg.ed25519_seed = s_coaps.ed25519_seed;
264 s->cfg.ephemeral_priv = s->eph;
265 s->cfg.server_random = s->srand;
266 s->cfg.cookie_key = s_coaps.cookie_key;
267 uint8_t paddr[PC_COAPS_PEER_SER];
268 bool ok = serialize_peer(ip, port, paddr);
269 pc_dtls_conn_init(&s->conn, &s->cfg, ok ? paddr : nullptr, ok ? sizeof paddr : 0);
270 copy_str(s->peer_ip,
sizeof s->peer_ip, ip);
276void udp_ingest_cb(
const uint8_t *data,
size_t len,
const pc_udp_peer *peer,
void * )
284 ring_push(data, len, ip, port);
290void coaps_route_datagram(
const CoapsIngest *ig, uint32_t now, uint8_t *out,
size_t out_cap)
297 bool cid_rec = ig->len >= 1 && (ig->data[0] & 0xE0) == 0x20 && (ig->data[0] & 0x10);
298 CoapsSlot *s = cid_rec ? slot_by_cid(ig->data + 1, ig->len - 1) : nullptr;
301 s = slot_by_peer(ig->ip, ig->port);
305 s = open_conn(ig->ip, ig->port);
312 if (cid_rec && (s->peer_port != ig->port || strcmp(s->peer_ip, ig->ip) != 0))
314 copy_str(s->peer_ip,
sizeof s->peer_ip, ig->ip);
315 s->peer_port = ig->port;
318 int n = pc_coaps_process(&s->conn, ig->data, ig->len, out, out_cap);
321 server_send(s->peer_ip, s->peer_port, out, (
size_t)n);
331void coaps_service_slot(CoapsSlot *s, uint32_t now, uint8_t *out,
size_t out_cap)
337 if (pc_dtls_conn_timeout_ms(&s->conn) == 0)
339 int n = pc_dtls_conn_on_timeout(&s->conn, out, out_cap);
342 server_send(s->peer_ip, s->peer_port, out, (
size_t)n);
359 if (now - s->last_ms >= PC_COAPS_IDLE_MS)
366bool pc_coaps_server_begin(uint16_t port,
const CoapsServerConfig *cfg)
368 if (!cfg || !cfg->rng || !cfg->cert_der || cfg->cert_len == 0)
372 s_coaps.cert_der = cfg->cert_der;
373 s_coaps.cert_len = cfg->cert_len;
374 memcpy(s_coaps.ed25519_seed, cfg->ed25519_seed,
sizeof s_coaps.ed25519_seed);
375 memcpy(s_coaps.cookie_key, cfg->cookie_key,
sizeof s_coaps.cookie_key);
376 s_coaps.rng = cfg->rng;
377 s_coaps.port = port ? port : PC_COAPS_PORT;
378 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
380 s_cpool.pool[i].used =
false;
382 s_coaps.ring_head = 0;
383 s_coaps.ring_tail = 0;
384 s_coaps.running =
true;
392void pc_coaps_server_poll()
394 if (!s_coaps.running)
399 uint8_t out[PC_COAPS_OUT_CAP];
404 while (ring_pop(&ig))
406 coaps_route_datagram(&ig, now, out,
sizeof out);
410 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
412 coaps_service_slot(&s_cpool.pool[i], now, out,
sizeof out);
416uint8_t pc_coaps_server_active_conns()
419 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
421 if (s_cpool.pool[i].used)
429void pc_coaps_server_stop()
431 s_coaps.running =
false;
432 for (uint8_t i = 0; i < PC_COAPS_MAX_CONNS; i++)
434 s_cpool.pool[i].used =
false;
436 s_coaps.ring_head = 0;
437 s_coaps.ring_tail = 0;
441void pc_coaps_server_set_out_sink_cb(CoapsServerOutFn fn,
void *ctx)
443 s_coaps.out_sink = fn;
444 s_coaps.out_ctx = ctx;
447bool pc_coaps_server_ingest(
const uint8_t *datagram,
size_t len,
const char *ip, uint16_t port)
449 return ring_push(datagram, len, ip, port);
Pluggable monotonic clock for all library timing.
uint32_t pc_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.
Shared single-producer / single-consumer byte-ring primitive.
bool pc_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.
bool pc_udp_listen(uint16_t port, pc_udp_handler handler, void *ctx)
Bind a UDP port and route incoming datagrams to handler.
bool pc_udp_peer_addr(const struct pc_udp_peer *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.
Layer 4 (Transport) - connectionless UDP datagram service.