For a request set up with coap_add_data_large_request_app(), the data
is provided by the application's get_func and there is no data pointer.
Three places still took the body from that NULL pointer, so
coap_add_data() was passed a NULL pointer:
- check_freshness(), re-sending the next block with the Echo option
from a 4.01 response.
- coap_cancel_observe_lkd(), re-adding the observe request's body to
the cancel request.
- coap_add_data_large_internal(), when the application sets a Block1
option with a non-zero block number to send a single block.
Use get_func in all of these, as the other places that send a block of
an lg_xmit already do. The single block and the whole-body cases now
share add_get_func_data(), which also checks the allocation and no
longer leaks the buffer when get_func fails.
Add unit tests.
With track_client_session set, an incoming session gets its own proxy
entry for each upstream server it uses. coap_proxy_remove_association()
returned after the first entry tracking the session, so the proxy
requests of that session held in later entries were left in place when
the session was deleted, and their upstream sessions were not released.
A later upstream response was then forwarded to the freed session.
Carry on with the remaining entries instead of returning.
Add a unit test.
Reduce value of partial timeout if there are a lot of outstanding
large block receipts that are not active.
Add function coap_context_set_max_bodies_ram() to define an upper memory
limit usage across all active body receipts.
Examples are only built when the library mode they depend on is enabled,
otherwise their link step fails with undefined references. The tiny example
is gated on client mode only.
Signed-off-by: warter666 <271496918+warter666@users.noreply.github.com>
`coap_proxy_get_add_list_entry(incoming, ongoing, ...)` dereferences `ongoing->context` when `incoming == NULL`. Current in-tree callers always pass exactly one non-NULL session, but the function is a public-ish internal helper and a future caller passing both NULL would dereference NULL immediately. cppcheck flags exactly this (`nullPointer` on `ongoing`).
This adds an explicit early `return NULL` when both arguments are NULL - defensive only, no behavior change for existing callers.
Signed-off-by: warter666 <121139223+Warterkk@users.noreply.github.com>
Three format-string/argument type mismatches in `src/coap_debug.c`, all reported by `-Wall -Wextra` (fatal under -Werror):
1. `msg_code_string()`: `"%u.%02u"` receives `int` expressions (uint16_t promotes to int) - cast both to `unsigned`.
2. `print_content_format()`: `"%d"` receives `unsigned int format_type` - use `%u`.
3. block-size printing: `"%u"` receives the `int` expression `1 << (...)` - make the shift and cast unsigned.
No behavior change on any realistic input; only the int-vs-unsigned variance is fixed.
Signed-off-by: warter666 <121139223+Warterkk@users.noreply.github.com>
With `-DENABLE_CLIENT_MODE=OFF` (i.e. `COAP_CLIENT_SUPPORT=0`), `full_match()` in `src/coap_block.c` is defined but its only caller is compiled out, producing `-Wunused-function` (fatal in -Werror builds):
```
src/coap_block.c:479:1: error: unused function 'full_match' [-Werror=-Wunused-function]
```
Wrap the static-inline definition in the same `#if COAP_CLIENT_SUPPORT` guard as its caller.
Signed-off-by: warter666 <121139223+Warterkk@users.noreply.github.com>
In `coap_ws_rd_http_header_client()` the first header (the HTTP status line) is split by `coap_ws_split_rd_header()`, which returns NULL when the buffered line contains no space/tab separator. The second call site checks for NULL, but the first one does not:
```c
value = coap_ws_split_rd_header(session);
if (strcmp((char *)ws->http_hdr, "HTTP/1.1") != 0 ||
atoi(value) != 101) {
```
If the HTTP header buffer is exactly `"HTTP/1.1"` (no trailing space), `strcmp()` returns 0, the `||` short-circuit does not apply, and `atoi(NULL)` is called - a remote crash in the WS client handshake when a malicious or broken server sends a truncated status line.
Fix: return 0 when `value` is NULL, mirroring the second call site.
The library builds cleanly after the change (cmake -DENABLE_DTLS=OFF, gcc/clang, 0 warnings).
Signed-off-by: warter666 <121139223+Warterkk@users.noreply.github.com>
When a message spans multiple mbedTLS records, coap_tls_write() returns
only the final mbedtls_ssl_write() result despite sending the entire
message. Its caller treats the PDU as partially written and queues an
already transmitted suffix.
Update the successful return value to the accumulated byte count.
Preserve the existing nonblocking retry and fatal-error handling.
Validated with native mbedTLS 3.6.2 loopback transfers and a focused
ASan/UBSan harness covering record boundaries and WANT_READ/WANT_WRITE.
TCP BERT payloads can exceed the 1024-byte callback scratch buffer. Allocate a temporary buffer sized to the requested chunk, pass it to the large-data callback, and release it after the PDU has copied the data. Preserve the stack buffer for regular block transfers and report allocation failures explicitly.
Packets from the same peer can overwrite a session's local address with a
multicast destination. Save the address when an observation is created or
renewed and restore it before generating notifications, so deferred responses
snapshot the correct address instead of being dropped as invalid multicast.
Add regression coverage for CON and NON observations, including renewals and
the local address captured by a notification's async response.
Validation: 215 unit tests passed (1712 assertions).
A request handler that leaves the response code unset asks for no
response to be sent. That is honoured for a piggybacked ACK (the token
gets stripped, so an empty ACK goes out) and for a non-confirmable or
reliable response (dropped in no_response()), but not for a separate
response set up with coap_register_async().
There the response is turned into a CON, and a 0.00 CON was put on the
wire. An Empty message is a ping (RFC 7252 4.2), not a response, and the
PDU still carries the request's token, which RFC 7252 4.1 does not allow.
Being confirmable, it was then retransmitted up to MAX_RETRANSMIT times,
usually at a client port that had long gone away.
Drop it like the other two cases, delete it without logging it as a
dropped response since the application asked for this, and document in
coap_handler(3) that a separate response is not sent either.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>