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,32 +1808,63 @@ struct coap_reply *coap_response_received(
{ {
struct coap_reply *r; struct coap_reply *r;
uint8_t token[COAP_TOKEN_MAX_LEN]; uint8_t token[COAP_TOKEN_MAX_LEN];
bool piggybacked = false;
uint8_t type;
uint16_t id; uint16_t id;
uint8_t tkl; uint8_t tkl;
size_t i; size_t i;
if (!is_empty_message(response) && coap_packet_is_request(response)) { type = coap_header_get_type(response);
/* Request can't be 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; return NULL;
} }
id = coap_header_get_id(response); if (type == COAP_TYPE_ACK) {
tkl = coap_header_get_token(response, token); piggybacked = true;
}
for (i = 0, r = replies; i < len; i++, r++) { for (i = 0, r = replies; i < len; i++, r++) {
int age; int age;
if ((r->id == 0U) && (r->tkl == 0U)) { /* Skip unused entry. */
if (r->reply == NULL) {
continue; continue;
} }
/* Piggybacked must match id when token is empty */ /* Reset should only be handled if Message ID matches. */
if ((r->id != id) && (tkl == 0U)) { if (type == COAP_TYPE_RESET) {
if (r->id != id) {
continue;
}
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; continue;
} }
if (tkl > 0 && memcmp(r->token, token, tkl)) { if (r->tkl > 0) {
continue; if (memcmp(r->token, token, r->tkl) != 0) {
continue;
}
} }
age = coap_get_option_int(response, COAP_OPTION_OBSERVE); age = coap_get_option_int(response, COAP_OPTION_OBSERVE);
@ -1841,6 +1872,7 @@ struct coap_reply *coap_response_received(
if (age == -ENOENT || coap_age_is_newer(r->age, age)) { if (age == -ENOENT || coap_age_is_newer(r->age, age)) {
r->age = age; r->age = age;
if (coap_header_get_code(response) != COAP_RESPONSE_CODE_CONTINUE) { if (coap_header_get_code(response) != COAP_RESPONSE_CODE_CONTINUE) {
handle_reply:
r->reply(response, r, from); r->reply(response, r, from);
} }
} }