F-7658 - Validate DISCONNECT reason codes

pull/577/head
Aidan Garske 2026-08-20 20:08:06 -07:00
parent 7a3f708b3c
commit fe5541b46d
2 changed files with 133 additions and 0 deletions

View File

@ -251,6 +251,46 @@ int MqttPacket_SubAckReturnCodeValid(byte code, byte protocol_level)
}
#ifdef WOLFMQTT_V5
/* [MQTT-3.14.2-1] Validate a DISCONNECT Reason Code against Table 3-10. */
static int MqttPacket_DisconnectReasonCodeValid(byte code)
{
switch (code) {
case MQTT_REASON_SUCCESS: /* 0x00 */
case MQTT_REASON_DISCONNECT_W_WILL_MSG: /* 0x04 */
case MQTT_REASON_UNSPECIFIED_ERR: /* 0x80 */
case MQTT_REASON_MALFORMED_PACKET: /* 0x81 */
case MQTT_REASON_PROTOCOL_ERR: /* 0x82 */
case MQTT_REASON_IMPL_SPECIFIC_ERR: /* 0x83 */
case MQTT_REASON_NOT_AUTHORIZED: /* 0x87 */
case MQTT_REASON_SERVER_BUSY: /* 0x89 */
case MQTT_REASON_SERVER_SHUTTING_DOWN: /* 0x8B */
case MQTT_REASON_KEEP_ALIVE_TIMEOUT: /* 0x8D */
case MQTT_REASON_SESSION_TAKEN_OVER: /* 0x8E */
case MQTT_REASON_TOPIC_FILTER_INVALID: /* 0x8F */
case MQTT_REASON_TOPIC_NAME_INVALID: /* 0x90 */
case MQTT_REASON_RX_MAX_EXCEEDED: /* 0x93 */
case MQTT_REASON_TOPIC_ALIAS_INVALID: /* 0x94 */
case MQTT_REASON_PACKET_TOO_LARGE: /* 0x95 */
case MQTT_REASON_MSG_RATE_TOO_HIGH: /* 0x96 */
case MQTT_REASON_QUOTA_EXCEEDED: /* 0x97 */
case MQTT_REASON_ADMIN_ACTION: /* 0x98 */
case MQTT_REASON_PAYLOAD_FORMAT_INVALID: /* 0x99 */
case MQTT_REASON_RETAIN_NOT_SUPPORTED: /* 0x9A */
case MQTT_REASON_QOS_NOT_SUPPORTED: /* 0x9B */
case MQTT_REASON_USE_ANOTHER_SERVER: /* 0x9C */
case MQTT_REASON_SERVER_MOVED: /* 0x9D */
case MQTT_REASON_SS_NOT_SUPPORTED: /* 0x9E */
case MQTT_REASON_CON_RATE_EXCEED: /* 0x9F */
case MQTT_REASON_MAX_CON_TIME: /* 0xA0 */
case MQTT_REASON_SUB_ID_NOT_SUP: /* 0xA1 */
case MQTT_REASON_WILDCARD_SUB_NOT_SUP: /* 0xA2 */
return 1;
default:
break;
}
return 0;
}
/* [MQTT-4.8.0-1] Validate a v5 UNSUBACK Reason Code against the fixed set in
* MQTT 5.0 section 3.11.3. Returns 1 if allowed, 0 if reserved. */
static int MqttPacket_UnsubAckReasonCodeValid(byte code)
@ -3681,6 +3721,9 @@ int MqttEncode_Disconnect(byte *tx_buf, int tx_buf_len,
if ((disconnect != NULL) &&
(disconnect->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5)) {
if (!MqttPacket_DisconnectReasonCodeValid(disconnect->reason_code)) {
return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
}
if (disconnect->props != NULL) {
/* Determine length of properties */
props_len = MqttEncode_Props(MQTT_PACKET_TYPE_DISCONNECT,

View File

@ -4924,6 +4924,94 @@ TEST(decode_disconnect_v5_reason_code_past_buf_rejected)
rc = MqttDecode_Disconnect(buf, (int)sizeof(buf), &disc);
ASSERT_EQ(MQTT_CODE_ERROR_OUT_OF_BUFFER, rc);
}
/* [MQTT-3.14.2-1] Table 3-10 is the complete DISCONNECT Reason Code
* allow-list. Each nonzero code is encoded as the sole variable-header byte. */
TEST(encode_disconnect_v5_reason_code_allowed_set)
{
static const byte allowed[] = {
MQTT_REASON_SUCCESS,
MQTT_REASON_DISCONNECT_W_WILL_MSG,
MQTT_REASON_UNSPECIFIED_ERR,
MQTT_REASON_MALFORMED_PACKET,
MQTT_REASON_PROTOCOL_ERR,
MQTT_REASON_IMPL_SPECIFIC_ERR,
MQTT_REASON_NOT_AUTHORIZED,
MQTT_REASON_SERVER_BUSY,
MQTT_REASON_SERVER_SHUTTING_DOWN,
MQTT_REASON_KEEP_ALIVE_TIMEOUT,
MQTT_REASON_SESSION_TAKEN_OVER,
MQTT_REASON_TOPIC_FILTER_INVALID,
MQTT_REASON_TOPIC_NAME_INVALID,
MQTT_REASON_RX_MAX_EXCEEDED,
MQTT_REASON_TOPIC_ALIAS_INVALID,
MQTT_REASON_PACKET_TOO_LARGE,
MQTT_REASON_MSG_RATE_TOO_HIGH,
MQTT_REASON_QUOTA_EXCEEDED,
MQTT_REASON_ADMIN_ACTION,
MQTT_REASON_PAYLOAD_FORMAT_INVALID,
MQTT_REASON_RETAIN_NOT_SUPPORTED,
MQTT_REASON_QOS_NOT_SUPPORTED,
MQTT_REASON_USE_ANOTHER_SERVER,
MQTT_REASON_SERVER_MOVED,
MQTT_REASON_SS_NOT_SUPPORTED,
MQTT_REASON_CON_RATE_EXCEED,
MQTT_REASON_MAX_CON_TIME,
MQTT_REASON_SUB_ID_NOT_SUP,
MQTT_REASON_WILDCARD_SUB_NOT_SUP
};
MqttDisconnect disc;
byte buf[16];
size_t i;
for (i = 0; i < sizeof(allowed); i++) {
int rc;
XMEMSET(&disc, 0, sizeof(disc));
XMEMSET(buf, 0xA5, sizeof(buf));
disc.protocol_level = MQTT_CONNECT_PROTOCOL_LEVEL_5;
disc.reason_code = allowed[i];
rc = MqttEncode_Disconnect(buf, (int)sizeof(buf), &disc);
ASSERT_EQ(MQTT_PACKET_TYPE_SET(MQTT_PACKET_TYPE_DISCONNECT), buf[0]);
if (allowed[i] == MQTT_REASON_SUCCESS) {
ASSERT_EQ(2, rc);
ASSERT_EQ(0, buf[1]);
}
else {
ASSERT_EQ(3, rc);
ASSERT_EQ(1, buf[1]);
ASSERT_EQ(allowed[i], buf[2]);
}
}
}
/* [MQTT-3.14.2-1] Values assigned only to other packet types are reserved
* for DISCONNECT and must not be emitted. */
TEST(encode_disconnect_v5_reason_code_out_of_table_rejected)
{
static const byte invalid[] = {
MQTT_REASON_GRANTED_QOS_1,
MQTT_REASON_CONT_AUTH,
MQTT_REASON_BAD_AUTH_METHOD,
MQTT_REASON_PACKET_ID_NOT_FOUND,
0xFF
};
MqttDisconnect disc;
byte buf[16];
size_t i;
for (i = 0; i < sizeof(invalid); i++) {
int rc;
XMEMSET(&disc, 0, sizeof(disc));
XMEMSET(buf, 0xA5, sizeof(buf));
disc.protocol_level = MQTT_CONNECT_PROTOCOL_LEVEL_5;
disc.reason_code = invalid[i];
rc = MqttEncode_Disconnect(buf, (int)sizeof(buf), &disc);
ASSERT_EQ(MQTT_CODE_ERROR_BAD_ARG, rc);
ASSERT_EQ(0xA5, buf[0]);
}
}
#endif /* WOLFMQTT_V5 */
/* ============================================================================
@ -6052,6 +6140,8 @@ void run_mqtt_packet_tests(void)
RUN_TEST(decode_disconnect_v5_invalid_fixed_header_flags_rejected);
RUN_TEST(decode_disconnect_v5_with_reason_code_accepted);
RUN_TEST(decode_disconnect_v5_reason_code_past_buf_rejected);
RUN_TEST(encode_disconnect_v5_reason_code_allowed_set);
RUN_TEST(encode_disconnect_v5_reason_code_out_of_table_rejected);
#endif
/* Fixed-header reserved-flag validation [MQTT-2.2.2-2] */