From 5e86d78e6d1a09bac4fbf55f54b5e8bccdfa0297 Mon Sep 17 00:00:00 2001 From: Michiel De Witte Date: Wed, 2 Sep 2026 12:03:39 +0200 Subject: [PATCH] coap_net.c: Do not send an Empty message as a separate response 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) --- man/coap_handler.txt.in | 3 ++- src/coap_net.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/man/coap_handler.txt.in b/man/coap_handler.txt.in index b0e33888..044f122e 100644 --- a/man/coap_handler.txt.in +++ b/man/coap_handler.txt.in @@ -118,7 +118,8 @@ This _handler_ must not call *coap_send*(3) to send _response_pdu_. _response_pdu_ gets sent on return from _handler_, assuming the response code has been updated. If the response code was not updated, then an empty ACK packet will get sent for CON type requests or nothing for NON type -requests. +requests. Nothing gets sent for a separate response (*coap_async*(3)), as +an Empty message is only valid as an ACK or a RST. *NOTE:* Any data associated with _incoming_pdu_ is no longer be available after exiting this function as _incoming_pdu_ is deleted. In particular diff --git a/src/coap_net.c b/src/coap_net.c index 97fac703..dc9e7eaa 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -3573,8 +3573,9 @@ enum respond_t { RESPONSE_DEFAULT, RESPONSE_DROP, RESPONSE_SEND }; * change it to an empty ACK and @c RESPONSE_SEND so the client does not keep * on retrying. * - * Checks if the response code is 0.00 and if either the session is reliable or - * non-confirmable, @c RESPONSE_DROP is also returned. + * Checks if the response code is 0.00 and if the response is confirmable, + * non-confirmable, or the session is reliable, @c RESPONSE_DROP is also + * returned. An Empty confirmable message is a ping, not a response. * * Multicast response checking is also carried out. * @@ -3673,8 +3674,13 @@ no_response(coap_pdu_t *request, coap_pdu_t *response, } } else if (COAP_PDU_IS_EMPTY(response) && (response->type == COAP_MESSAGE_NON || + response->type == COAP_MESSAGE_CON || COAP_PROTO_RELIABLE(session->proto))) { - /* response is 0.00, and this is reliable or non-confirmable */ + /* Response is 0.00, and this is reliable, non-confirmable, or a separate + * (confirmable) response. An Empty CON is a ping (RFC 7252 4.2), not a + * response, and the PDU still has the request's token attached, which + * RFC 7252 4.1 does not allow. A CON would then get retransmitted up to + * MAX_RETRANSMIT times. */ return RESPONSE_DROP; } @@ -4352,6 +4358,7 @@ skip_handler: } } else if (COAP_PDU_IS_EMPTY(response) && (response->type == COAP_MESSAGE_NON || + response->type == COAP_MESSAGE_CON || COAP_PROTO_RELIABLE(session->proto))) { coap_delete_pdu_lkd(response); } else {