@@ -360,6 +360,88 @@ TEST(connect_v311_explicit_auto_prefix_refused)
360360 MqttBroker_Free (& broker );
361361}
362362
363+ /* [MQTT-3.1.2-2] Unsupported Protocol Level must be refused with CONNACK
364+ * 0x01 (REFUSED_PROTO) followed by disconnect. The CONNACK MUST come back
365+ * in v3.1.1 wire shape (4 bytes: type, remain=2, flags, code) regardless
366+ * of the level the client claimed — we don't know what their wire format
367+ * actually is, and the spec text specifies "CONNACK return code 0x01"
368+ * verbatim.
369+ *
370+ * Cases below mirror the dynamic test evidence in the issue report:
371+ * level 0x03 -> refused
372+ * level 0x06 -> refused (and not silently accepted as v5 just because
373+ * the encoder uses level >= 5 as a mode switch).
374+ */
375+ static void run_unsupported_level (byte level )
376+ {
377+ MqttBroker broker ;
378+ MqttBrokerNet net ;
379+ /* Wire matches the reporter's case for level X with ClientId "A":
380+ * 10 0d 00 04 4d 51 54 54 LL 02 00 3c 00 01 41
381+ * (15 bytes total: fixed header 2 + remain 13 = type+nameLen+name+
382+ * level+flags+keepalive+idLen+id) */
383+ byte connect [] = {
384+ 0x10 , 13 ,
385+ 0x00 , 0x04 , 'M' , 'Q' , 'T' , 'T' ,
386+ 0x00 , /* protocol_level placeholder */
387+ 0x02 , /* CleanSession=1 */
388+ 0x00 , 0x3C ,
389+ 0x00 , 0x01 , 'A'
390+ };
391+ connect [8 ] = level ;
392+
393+ install_mock_net (& net );
394+ XMEMSET (& broker , 0 , sizeof (broker ));
395+ ASSERT_EQ (MQTT_CODE_SUCCESS , MqttBroker_Init (& broker , & net ));
396+ ASSERT_EQ (MQTT_CODE_SUCCESS , MqttBroker_Start (& broker ));
397+
398+ reset_mock_state (connect , sizeof (connect ));
399+ run_broker_one_connect (& broker );
400+
401+ /* Expect a v3.1.1-shaped CONNACK: 0x20, 0x02, flags, REFUSED_PROTO.
402+ * [MQTT-3.1.2-2] mandates 0x01 for any unsupported level regardless of
403+ * what the client claimed they spoke. */
404+ ASSERT_TRUE (g_out_len >= 4 );
405+ ASSERT_EQ (0x20 , g_out_buf [0 ]);
406+ ASSERT_EQ (0x02 , g_out_buf [1 ]);
407+ ASSERT_EQ (0x00 , g_out_buf [2 ]);
408+ ASSERT_EQ (MQTT_CONNECT_ACK_CODE_REFUSED_PROTO , g_out_buf [3 ]);
409+ ASSERT_TRUE (g_client_closed );
410+
411+ MqttBroker_Stop (& broker );
412+ MqttBroker_Free (& broker );
413+ }
414+
415+ TEST (connect_unsupported_level_3_refused )
416+ {
417+ run_unsupported_level (0x03 );
418+ }
419+
420+ TEST (connect_unsupported_level_6_refused )
421+ {
422+ /* Catches the secondary issue the reporter flagged in section 3 of #496:
423+ * pre-fix MqttDecode_Connect treated `protocol_level >= 5` as v5, so for
424+ * level 6 the decoder consumed the byte at the v5 props_len position
425+ * (0x00 here, parsed as zero-length props), then read the next two bytes
426+ * (0x00 0x01) as the ClientId length prefix and 'A' as the start of a
427+ * 1-byte ClientId. With WOLFMQTT_V5 the decoder still returned success
428+ * for this particular wire, but it produced a misaligned MqttConnect with
429+ * ClientId="" — so the test below also fails on the post-decode path
430+ * unless the broker's [MQTT-3.1.2-2] check rejects the level. (For wires
431+ * without enough trailing bytes, the pre-fix decoder instead returned
432+ * OUT_OF_BUFFER and never emitted a CONNACK at all, which would also
433+ * fail the `g_out_len >= 4` assertion.) Either way, this test pins the
434+ * fix at both layers. */
435+ run_unsupported_level (0x06 );
436+ }
437+
438+ TEST (connect_unsupported_level_127_refused )
439+ {
440+ /* Top of the byte range — guards against a future "treat high values
441+ * as latest known" mutation. */
442+ run_unsupported_level (0x7F );
443+ }
444+
363445#ifdef WOLFMQTT_V5
364446/* v5 dropped the [MQTT-3.1.3-8] CleanSession=1-only restriction; an empty
365447 * ClientId is acceptable with any Clean Start value. The broker MUST emit
@@ -463,6 +545,9 @@ int main(int argc, char** argv)
463545 RUN_TEST (connect_v311_emptyid_clean1_accepted );
464546 RUN_TEST (connect_v311_nonempty_clean0_accepted );
465547 RUN_TEST (connect_v311_explicit_auto_prefix_refused );
548+ RUN_TEST (connect_unsupported_level_3_refused );
549+ RUN_TEST (connect_unsupported_level_6_refused );
550+ RUN_TEST (connect_unsupported_level_127_refused );
466551#ifdef WOLFMQTT_V5
467552 RUN_TEST (connect_v5_emptyid_assigned_id_emitted );
468553 RUN_TEST (connect_v5_emptyid_clean0_accepted );
0 commit comments