net: coap: Fix response matching algorithm

The algorithm for matching request with response was incorrect, which
could lead to false matches (for example if request had a token, and
piggybacked reply had no token but matching message ID only, that would
still be counted as a match).

This commit fixes it. The request/reply matching is implemented based on
RFC now, with separate conditions for piggybacked/separate responses.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2025-01-17 15:54:30 +01:00 committed by Benjamin Cabé
commit 13cd48a431

View file

@ -1808,39 +1808,71 @@ struct coap_reply *coap_response_received(
{
struct coap_reply *r;
uint8_t token[COAP_TOKEN_MAX_LEN];
bool piggybacked = false;
uint8_t type;
uint16_t id;
uint8_t tkl;
size_t i;
if (!is_empty_message(response) && coap_packet_is_request(response)) {
/* Request can't be response */
type = coap_header_get_type(response);
id = coap_header_get_id(response);
tkl = coap_header_get_token(response, token);
if ((type == COAP_TYPE_ACK && is_empty_message(response)) ||
coap_packet_is_request(response)) {
/* Request or empty ACK can't be response */
return NULL;
}
id = coap_header_get_id(response);
tkl = coap_header_get_token(response, token);
if (type == COAP_TYPE_ACK) {
piggybacked = true;
}
for (i = 0, r = replies; i < len; i++, r++) {
int age;
if ((r->id == 0U) && (r->tkl == 0U)) {
/* Skip unused entry. */
if (r->reply == NULL) {
continue;
}
/* Piggybacked must match id when token is empty */
if ((r->id != id) && (tkl == 0U)) {
/* Reset should only be handled if Message ID matches. */
if (type == COAP_TYPE_RESET) {
if (r->id != id) {
continue;
}
if (tkl > 0 && memcmp(r->token, token, tkl)) {
goto handle_reply;
}
/* In a piggybacked response, the Message ID of the Confirmable
* request and the Acknowledgment MUST match, and the tokens of
* the response and original request MUST match. In a separate
* response, just the tokens of the response and original request
* MUST match.
*/
if (piggybacked) {
if (r->id != id) {
continue;
}
}
if (r->tkl != tkl) {
continue;
}
if (r->tkl > 0) {
if (memcmp(r->token, token, r->tkl) != 0) {
continue;
}
}
age = coap_get_option_int(response, COAP_OPTION_OBSERVE);
/* handle observed requests only if received in order */
if (age == -ENOENT || coap_age_is_newer(r->age, age)) {
r->age = age;
if (coap_header_get_code(response) != COAP_RESPONSE_CODE_CONTINUE) {
handle_reply:
r->reply(response, r, from);
}
}