This changes the API of nanocoap with the goal to reduce the expose of
UDP specifics in the API. The plan is to eventually support transports
such as CoAP over TCP and CoAP over WebSocket directly in nanocoap
while sharing most of the code, as e.g. the CoAP Option processing
remains identical. Specifically, the plan is to unlock a transport with
modules and introduce overhead for dispatching to specific transport
only when multiple transports are actually in use.
Support for OSCORE directly in nanocoap is probably not sensible, as
the serialization is very much unlike the other transports. A unified
CoAP API for multiple transports including OSCORE is probably best
implemented on top. But when limited to the boring set of CoAP
transports, we probably can support them well with nanocoap with less
overhead.
Breaking API Changes:
=====================
- `coap_parse()` now returns `ssize_t` instead of `int`
- This function is not really user facing, so the impact should
be limited
- This is useful for stream transports where the buffer may
contain data of more than one packet. The return value contains
the number of bytes actually consumed, which will match the
buffer size for non-stream transports.
API Changes:
============
- `coap_pkt_t` now contains a `uint8_t *buf` pointer instead of a
`coap_hdr_t *hdr` pointer to the beginning of the buffer
- This will also work when the buffer is used by non-UDP
transports
- A deprecated `coap_udp_hdr_t *hdr` has been crammed into
an unnamed `union` with `uint8_t *buf`. For architectures
where pointers have the same memory layout regardless of type
(e.g. all of the supported ones), this will make `hdr` an
alias for `buf`.
- The alias will only be provided if no transport besides UDP is
used in nanocoap. So existing apps will continue to work, new
apps that want to support other transports need to move to
adapt.
- `coap_hdr_t` has been renamed to `coap_udp_hdr_t`
- A deprecated alias was created for deprecation
- `coap_hdr*()` functions have been deprecated
- Equivalent `coap_pkt*()` functions have been created that work
on `coap_pkt_t *` instead of `coap_hdr_t *`
- If non-UDP transports are used, the deprecated `coap_hdr*()`
will probably not be exposed to avoid footguns.
- `coap_build_hdr()` has been renamed to `coap_build_udp_hdr()` and
that works on an `uint8_t *` buffer with a given length, rather than
on a `coap_hdr_t *` with a *figers crossed* length
- a deprecated `coap_build_hdr()` function was added that calls
to `coap_build_udp_hdr()` and has the same signature, so that
users have time to update
- The responsibility for handling matching CoAP No-Response Options
has been split:
- `coap_build_reply()` only needs to report this and return
`-ECANCLED`
- `coap_handle_req()` does generate the empty ACK is needed.
==> As a result, writing CoAP request handlers correctly becomes a
lost easier. Correct error handling to be present is now
sufficient for correct handling of No-Response options.
==> This change is backward compatible with existing code.
- The API doc has been cleaned up and straightened
Co-authored-by: mguetschow <mikolai.guetschow@tu-dresden.de>
For in-band signalling that a content format is not valid / present,
the magic number `COAP_FORMAT_NONE` was introduced and the type
`uint16_t` was used. Some APIs however used different in-band signalling
values and types:
- coap_reply_simple(): No signal available, `unsigned int`
- coap_build_reply_header(): negative values, `int`
(Using `int` would prevent using larger content format numbers on
8-bit and 16-bit archs, where `int` and `int16_t` have the same
range.)
This changes the behavior to consistently use `COAP_FORMAT_NONE` as
"no content format" signal and `uint16_t` as type.
`coap_request_ctx_get_local()` may return `NULL` depending on module
selection. If it does, we cannot pass `NULL` to
`sock_udp_ep_is_multicast()`.
This changes the code assume the request is not received via multicast
when module `sock_aux_local` is not in use.
When the user binds the CoAP server to multiple endpoints (as needed
for using multicast), it is required that `sock_aux_local` is in use
anyway, as otherwise the server may not respond using the endpoint it
received the request on. (That in turn would render the client unable to
correlate the response to the request.)
- Do not reply with a reset message to a reset or an ACK message
- Reply with a reset message when not able to process a CON/NON message
(not even a suitable error reply)
This adds the new `nanocoap_server_observe` module that implements the
server side of the CoAP Observe option. It does require cooperation
from the resource handler to work, though.
Co-Authored-By: mguetschow <mikolai.guetschow@tu-dresden.de>
Co-authored-by: benpicco <benpicco@googlemail.com>
This allows sending a separate response with CoAP Options and adds a
helper to detect duplicate requests, so that resource handlers can
repeat their empty ACK on duplicates.
When module `nanocoap_server_separate` is not used, the functions to
send separate responses are still provided, just in a broken version:
They will send the separate replies from a different endpoint than the
request was received at (even on machines with only one IP address, as
also the source port is randomized).
This changes the behavior to only provide the functions for separate
response when the do work, so that others will detect an invalid
configuration at compile time rather than at run time.
The documentation is duly updated.
An RST message has no token, so don't reply with a token when sending
RST.
This also adds unit tests to ensure this this exact bug does not sneak
back in.
When RFC 8974 support (module `nanocoap_token_ext`) is in use, the
request token may be longer than the buffer in the separate response
context is large. This adds a check to not overflow the buffer.
Sadly, this is an API change: Preparing the separate response context
can actually fail, so we need to report this with a return value.
The example application has been adapted to only proceed if the separate
reply context could have been prepared, and rather directly emit a
reset message if the token exceeds the static buffer.
Co-authored-by: benpicco <benpicco@googlemail.com>
Before, handlers writing blockwise transfer assumed that the response
header length will match the request header length. This is true for
UDP, but not for TCP: The CoAP over TCP header contains a Len field,
that gets extended for larger messages. Since the reply often is indeed
larger than the request, this is indeed often the case for CoAP over
TCP.
Note: Right now, no CoAP over TCP implementation is upstream. However,
getting rid of incorrect assumptions now will make life easier
later on.
In case no payload is added, `coap_build_reply_header()` would return
`sizeof(coap_hdr_t) + token_length` regardless of the actual header
length returned by `coap_build_hdr()`. These can be different if
RFC 8974 extended tokens are enabled (module `nanocoap_token_ext`
used): If an extended token length field is used, its size is not
considered.
Co-authored-by: benpicco <benpicco@googlemail.com>
Some calls to `coap_build_hdr()` were done with the target buffer for
the header and the source buffer for the token overlapping:
They reuse the buffer that held the request to assemble the response in.
We cannot use `memcpy()` in this case to copy the token into the target
buffer, as source and destination would (fully) overlap.
This commit makes reusing the request buffer for the response a special
case: `memcpy()` is only used to copy the token if source and
destination address of the token differ.
An alternative fix would have been to use `memmove()` unconditionally.
But `memmove()` does not make any assumption about the layout of target
and source buffer, while we know that the token either will already be
at the right position (when reusing the request buffer for the response)
or be in a non-overlapping buffer (when generating a fresh token). This
approach is more efficient than `memmove()`.
The CoAP block option gets written twice:
First a 'dummy' value is written by `coap_opt_add_block2()`, later this gets
overwritten by the real option value by coap_block2_finish().
The problem arises when the size of the option changes.
If the option ends up smaller than the dummy, we have garbage bytes after the
real option value, corrupting the packet.
To mitigate this, always write at least one option byte (which will be a 0 byte)
to ensure the dummy data is overwritten.
fixes#20686