in_mqtt: QoS 2 flow never completes (PUBREL ignored), SUBSCRIBE never answered, second CONNECT accepted, Session Present always 1
in_mqtt's server-side state machine handles only CONNECT, PUBLISH, PINGREQ and DISCONNECT; every other packet type is silently dropped, and CONNECT is handled the same way regardless of connection state. This breaks several mandatory server actions of MQTT 3.1.1.
Details
mqtt_prot_parser() dispatches:
if (conn->packet_type == MQTT_CONNECT) {
mqtt_handle_connect(conn);
}
else if (conn->packet_type == MQTT_PUBLISH) { ... }
else if (conn->packet_type == MQTT_PINGREQ) { mqtt_handle_ping(conn); }
else if (conn->packet_type == MQTT_DISCONNECT) { ... return MQTT_HANGUP; }
else {
}- PUBREL is never answered with PUBCOMP. For QoS 2,
mqtt_handle_publish()sends PUBREC, so the client continues with PUBREL; that packet lands in the emptyelseand no PUBCOMP is sent. MQTT 3.1.1 §3.7: PUBCOMP "is the response to a PUBREL Packet"; §4.3.3 requires the receiver to "respond to a PUBREL packet by sending a PUBCOMP packet containing the same Packet Identifier as the PUBREL" [MQTT-4.3.3-2]. The client keeps the message in flight and, per §4.4, re-sends PUBREL on reconnect. - SUBSCRIBE / UNSUBSCRIBE are never answered. §3.8.4: "When the Server receives a SUBSCRIBE Packet from a Client, the Server MUST respond with a SUBACK Packet" [MQTT-3.8.4-1] (the SUBACK can carry the failure code 0x80); §3.10.4 likewise for UNSUBACK. A client that subscribes waits indefinitely.
- A second CONNECT is accepted. The only state check is
conn->status == MQTT_NEW && packet_type != MQTT_CONNECT(L393); in stateMQTT_NEXTa CONNECT is answered with another CONNACK. §3.1.0: "The Server MUST process a second CONNECT Packet sent from a Client as a protocol violation and disconnect the Client" [MQTT-3.1.0-2]. - Session Present is always 1.
mqtt_handle_connect()sets bit 0 of the acknowledge flags unconditionally (BIT_SET(buf[i], 0)) and never parses the CONNECT. §3.2.2.2: if the server accepts a connection with CleanSession = 1 it MUST set Session Present to 0 [MQTT-3.2.2-1]; with CleanSession = 0 it may only be 1 if the server has stored session state, whichin_mqttnever keeps. - Fixed-header flags / CONNECT contents are not validated (lower priority): flags of CONNECT/PINGREQ/DISCONNECT (must be
0000) are not checked [MQTT-2.2.2-2], and the CONNECT protocol name/level are never examined, so e.g. MQTT 5 clients are accepted with a 3.1.1 CONNACK instead of being refused with return code 0x01.
Trigger / observed result
Against an unmodified build (default in_mqtt settings). Every session starts with CONNECT 10 0d 00 04 4d 51 54 54 04 02 00 3c 00 01 63 (MQTT 3.1.1, CleanSession=1):
| Client sends | Server reply observed | Expected |
|---|---|---|
| CONNECT (CleanSession=1) | 20 02 01 00 (Session Present = 1) |
20 02 00 00 |
PUBLISH QoS 2 id 7 34 0c 00 01 74 00 07 … |
50 02 00 07 (PUBREC) |
PUBREC |
then PUBREL 62 02 00 07 |
nothing within 1.5 s | PUBCOMP 70 02 00 07 |
SUBSCRIBE 82 06 00 01 00 01 74 00 |
nothing within 1.0 s | SUBACK (e.g. 90 03 00 01 80) |
| second CONNECT on the same connection | 20 02 01 00 (another CONNACK) |
disconnect |
PINGREQ with flags 1111 (cf 00) |
d0 00 (PINGRESP) |
disconnect |
Reproduction files: issues/verification/fluentbit_inputs/ (send_cases.py, client_result.txt).
Impact
Interoperability / reliability. Standard clients publishing with QoS 2 never complete the exchange: the message is ingested once at PUBLISH time, but the client's in-flight window stays occupied and PUBREL is re-sent after reconnects; clients that (even incidentally) subscribe hang. Session Present = 1 tells a clean-session client that the server resumed a session it does not have. No memory-safety impact.
Suggested fix
In mqtt_prot_parser(): answer MQTT_PUBREL (flags must be 0010) with PUBCOMP carrying the same Packet Identifier; answer SUBSCRIBE with a SUBACK containing 0x80 for each filter (or close the connection if subscriptions are intentionally unsupported) and UNSUBSCRIBE with UNSUBACK; treat CONNECT in state MQTT_NEXT as a protocol error; in mqtt_handle_connect() send Session Present = 0 and validate protocol name "MQTT"/level 4 (return code 0x01 otherwise); reject invalid fixed-header flags.
Source: fluent/fluent-bit