Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MQTT v5 PUBACK with reason code 16 triggers error handler with 'message too long' error #650

Closed
shantanu1singh opened this issue Jul 27, 2020 · 12 comments

Comments

@shantanu1singh
Copy link

redboltz/mqtt_cpp Version: v7.0.1

I'm using the v5 client of the library to connect to the Mosquitto MQTT broker and publish a message.

The Mosquitto broker returns a PUBACK with a reason code of 16 i.e., no matching subscribers found.

Related entry from mosquitto broker's logs:

1595881968: Received PUBLISH from v5_client (d0, q1, r0, m2, 'hub/telemetry', ... (67 bytes))
1595881968: Sending PUBACK to v5_client (m2, rc16)

The client library triggers the error_handler set using set_error_handler with a boost error_code of 90 i.e., 'Message too long'.
It should instead have invoked the pub_ack_handler (set using set_v5_puback_handler) and provided the puback_reason_code of 16 as per the Mqtt v5 specification.

Is this a known issue?

I tried going through the code to see where the library returns the 'message too long' error code and found a few places so wasn't sure how to pinpoint the location where it invoked the error_handler from. Any tips to debug will be appreciated!

@jonesmz
Copy link
Contributor

jonesmz commented Jul 27, 2020

I recommend that the first thing you do is create a minimal reproduction.

Could you add a new unit test to the existing test suite?

Second: You're probably going to want to look at the properties.hpp code. There are some areas there that I identified as being non-conformant, and they had backwards compatibility issues.

EDIT: There are comments in the code indicating the risky areas.

@redboltz
Copy link
Owner

Is this a known issue?

No.

Here is v5 example code:
https://github.com/redboltz/mqtt_cpp/blob/master/example/v5_no_tls_both.cpp

You can modify both client and server side.

In order to modify the server return no_matching_subscribers, you need to do as follows:

  1. Set pub response to manual mode.
  2. Send puback/pubrec with no_matching_subscribers reason code at the server side publish handler.
  3. Add pubrel handler to the server.

Here is diff:

index 978d20a..ffc035d 100644
--- a/example/v5_no_tls_both.cpp
+++ b/example/v5_no_tls_both.cpp
@@ -208,6 +208,8 @@ void server_proc(Server& s, std::set<con_sp_t>& connections, mi_sub_con& subs) {
             // including close_handler and error_handler.
             ep.start_session(std::make_tuple(std::move(spep), std::move(g)));

+            ep.set_auto_pub_response(false, false);
+
             // set connection (lower than MQTT) level handlers
             ep.set_close_handler(
                 [&connections, &subs, wp]
@@ -292,7 +294,7 @@ void server_proc(Server& s, std::set<con_sp_t>& connections, mi_sub_con& subs) {
                     return true;
                 });
             ep.set_v5_publish_handler( // use v5 handler
-                [&subs]
+                [&ep, &subs]
                 (MQTT_NS::optional<packet_id_t> packet_id,
                  MQTT_NS::publish_options pubopts,
                  MQTT_NS::buffer topic_name,
@@ -323,8 +325,32 @@ void server_proc(Server& s, std::set<con_sp_t>& connections, mi_sub_con& subs) {
                             std::move(props)
                         );
                     }
+
+                    switch (pubopts.get_qos()) {
+                    case MQTT_NS::qos::at_least_once:
+                        ep.puback(packet_id.get(), MQTT_NS::v5::puback_reason_code::no_matching_subscribers);
+                        break;
+                    case MQTT_NS::qos::exactly_once:
+                        ep.pubrec(packet_id.get(), MQTT_NS::v5::pubrec_reason_code::no_matching_subscribers);
+                        break;
+                    default:
+                        break;
+                    };
+
                     return true;
                 });
+            ep.set_v5_pubrel_handler(
+                [&ep]
+                (packet_id_t packet_id,
+                 MQTT_NS::v5::pubrel_reason_code reason_code,
+                 MQTT_NS::v5::properties /*props*/) {
+                    locked_cout() << "[server] pubrel received."
+                                  << " packet_id: " << packet_id
+                                  << " reason_code: " << reason_code << std::endl;
+                    ep.pubcomp(packet_id);
+                    return true;
+                }
+            );
             ep.set_v5_subscribe_handler( // use v5 handler
                 [&subs, wp]
                 (packet_id_t packet_id,

And here is whole updated code:

// Copyright Takatoshi Kondo 2019
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// no_tls client and server

#include <iostream>
#include <iomanip>
#include <map>

#include <mqtt_client_cpp.hpp>

#include <boost/lexical_cast.hpp>

#include "locked_cout.hpp"

template <typename Client, typename Disconnect>
void client_proc(
    Client& c,
    std::uint16_t& pid_sub1,
    std::uint16_t& pid_sub2,
    Disconnect const& disconnect) {

    using packet_id_t = typename std::remove_reference_t<decltype(*c)>::packet_id_t;
    // Setup client
    c->set_client_id("cid1");
    c->set_clean_start(true);

    // Setup handlers
    c->set_v5_connack_handler( // use v5 handler
        [&c, &pid_sub1, &pid_sub2]
        (bool sp, MQTT_NS::v5::connect_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            locked_cout() << "[client] Connack handler called" << std::endl;
            locked_cout() << "[client] Session Present: " << std::boolalpha << sp << std::endl;
            locked_cout() << "[client] Connect Reason Code: " << reason_code << std::endl;
            if (reason_code == MQTT_NS::v5::connect_reason_code::success) {
                pid_sub1 = c->subscribe("mqtt_client_cpp/topic1", MQTT_NS::qos::at_most_once);
                pid_sub2 = c->subscribe(
                    std::vector<std::tuple<MQTT_NS::string_view, MQTT_NS::subscribe_options>>
                    {
                        { "mqtt_client_cpp/topic2_1", MQTT_NS::qos::at_least_once },
                        { "mqtt_client_cpp/topic2_2", MQTT_NS::qos::exactly_once }
                    }
                );
            }
            return true;
        });
    c->set_close_handler( // this handler doesn't depend on MQTT protocol version
        []
        (){
            locked_cout() << "[client] closed." << std::endl;
        });
    c->set_error_handler( // this handler doesn't depend on MQTT protocol version
        []
        (MQTT_NS::error_code ec){
            locked_cout() << "[client] error: " << ec.message() << std::endl;
        });
    c->set_v5_puback_handler( // use v5 handler
        [&]
        (packet_id_t packet_id, MQTT_NS::v5::puback_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            locked_cout() <<
                "[client] puback received. packet_id: " << packet_id <<
                " reason_code: " << reason_code << std::endl;
            disconnect();
            return true;
        });
    c->set_v5_pubrec_handler( // use v5 handler
        [&]
        (packet_id_t packet_id, MQTT_NS::v5::pubrec_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            locked_cout() <<
                "[client] pubrec received. packet_id: " << packet_id <<
                " reason_code: " << reason_code << std::endl;
            return true;
        });
    c->set_v5_pubcomp_handler( // use v5 handler
        [&]
        (packet_id_t packet_id, MQTT_NS::v5::pubcomp_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            locked_cout() <<
                "[client] pubcomp received. packet_id: " << packet_id <<
                " reason_code: " << reason_code << std::endl;
            disconnect();
            return true;
        });
    c->set_v5_suback_handler( // use v5 handler
        [&]
        (packet_id_t packet_id,
         std::vector<MQTT_NS::v5::suback_reason_code> reasons,
         MQTT_NS::v5::properties /*props*/){
            locked_cout() << "[client] suback received. packet_id: " << packet_id << std::endl;
            for (auto const& e : reasons) {
                switch (e) {
                case MQTT_NS::v5::suback_reason_code::granted_qos_0:
                    locked_cout() << "[client] subscribe success: qos0" << std::endl;
                    break;
                case MQTT_NS::v5::suback_reason_code::granted_qos_1:
                    locked_cout() << "[client] subscribe success: qos1" << std::endl;
                    break;
                case MQTT_NS::v5::suback_reason_code::granted_qos_2:
                    locked_cout() << "[client] subscribe success: qos2" << std::endl;
                    break;
                default:
                    locked_cout() << "[client] subscribe failed: reason_code = " << static_cast<int>(e) << std::endl;
                    break;
                }
            }
            if (packet_id == pid_sub1) {
                c->publish("mqtt_client_cpp/topic1", "test1", MQTT_NS::qos::at_most_once);
            }
            else if (packet_id == pid_sub2) {
                c->publish("mqtt_client_cpp/topic2_1", "test2_1", MQTT_NS::qos::at_least_once);
                c->publish("mqtt_client_cpp/topic2_2", "test2_2", MQTT_NS::qos::exactly_once);
            }
            return true;
        });
    c->set_v5_publish_handler( // use v5 handler
        [&]
        (MQTT_NS::optional<packet_id_t> packet_id,
         MQTT_NS::publish_options pubopts,
         MQTT_NS::buffer topic_name,
         MQTT_NS::buffer contents,
         MQTT_NS::v5::properties /*props*/){
            locked_cout() << "[client] publish received. "
                          << " dup: "    << pubopts.get_dup()
                          << " qos: "    << pubopts.get_qos()
                          << " retain: " << pubopts.get_retain() << std::endl;
            if (packet_id)
                locked_cout() << "[client] packet_id: " << *packet_id << std::endl;
            locked_cout() << "[client] topic_name: " << topic_name << std::endl;
            locked_cout() << "[client] contents: " << contents << std::endl;
            disconnect();
            return true;
        });

    // Connect
    c->connect();
}

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <mqtt_server_cpp.hpp>

namespace mi = boost::multi_index;

using con_t = MQTT_NS::server<>::endpoint_t;
using con_sp_t = std::shared_ptr<con_t>;

struct sub_con {
    sub_con(MQTT_NS::buffer topic, con_sp_t con, MQTT_NS::qos qos_value, MQTT_NS::rap rap_value)
        :topic(std::move(topic)), con(std::move(con)), qos_value(qos_value), rap_value(rap_value) {}
    MQTT_NS::buffer topic;
    con_sp_t con;
    MQTT_NS::qos qos_value;
    MQTT_NS::rap rap_value;
};

struct tag_topic {};
struct tag_con {};

using mi_sub_con = mi::multi_index_container<
    sub_con,
    mi::indexed_by<
        mi::ordered_non_unique<
            mi::tag<tag_topic>,
            BOOST_MULTI_INDEX_MEMBER(sub_con, MQTT_NS::buffer, topic)
        >,
        mi::ordered_non_unique<
            mi::tag<tag_con>,
            BOOST_MULTI_INDEX_MEMBER(sub_con, con_sp_t, con)
        >
    >
>;


inline void close_proc(std::set<con_sp_t>& cons, mi_sub_con& subs, con_sp_t const& con) {
    cons.erase(con);

    auto& idx = subs.get<tag_con>();
    auto r = idx.equal_range(con);
    idx.erase(r.first, r.second);
}

template <typename Server>
void server_proc(Server& s, std::set<con_sp_t>& connections, mi_sub_con& subs) {
    s.set_error_handler( // this handler doesn't depend on MQTT protocol version
        [](MQTT_NS::error_code ec) {
            locked_cout() << "[server] error: " << ec.message() << std::endl;
        }
    );
    s.set_accept_handler( // this handler doesn't depend on MQTT protocol version
        [&s, &connections, &subs](con_sp_t spep) {
            auto& ep = *spep;
            std::weak_ptr<con_t> wp(spep);

            using packet_id_t = typename std::remove_reference_t<decltype(ep)>::packet_id_t;
            locked_cout() << "[server] accept" << std::endl;
            // For server close if ep is closed.
            auto g = MQTT_NS::shared_scope_guard(
                [&s] {
                    locked_cout() << "[server] session end" << std::endl;
                    s.close();
                }
            );
            // Pass spep to keep lifetime.
            // It makes sure wp.lock() never return nullptr in the handlers below
            // including close_handler and error_handler.
            ep.start_session(std::make_tuple(std::move(spep), std::move(g)));

            ep.set_auto_pub_response(false, false);

            // set connection (lower than MQTT) level handlers
            ep.set_close_handler(
                [&connections, &subs, wp]
                (){
                    locked_cout() << "[server] closed." << std::endl;
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    close_proc(connections, subs, sp);
                });
            ep.set_error_handler(
                [&connections, &subs, wp]
                (MQTT_NS::error_code ec){
                    locked_cout() << "[server] error: " << ec.message() << std::endl;
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    close_proc(connections, subs, sp);
                });

            // set MQTT level handlers
            ep.set_v5_connect_handler( // use v5 handler
                [&connections, wp]
                (MQTT_NS::buffer client_id,
                 MQTT_NS::optional<MQTT_NS::buffer> const& username,
                 MQTT_NS::optional<MQTT_NS::buffer> const& password,
                 MQTT_NS::optional<MQTT_NS::will>,
                 bool clean_start,
                 std::uint16_t keep_alive,
                 MQTT_NS::v5::properties /*props*/){
                    using namespace MQTT_NS::literals;
                    locked_cout() << "[server] client_id    : " << client_id << std::endl;
                    locked_cout() << "[server] username     : " << (username ? username.value() : "none"_mb) << std::endl;
                    locked_cout() << "[server] password     : " << (password ? password.value() : "none"_mb) << std::endl;
                    locked_cout() << "[server] clean_start  : " << std::boolalpha << clean_start << std::endl;
                    locked_cout() << "[server] keep_alive   : " << keep_alive << std::endl;
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    connections.insert(sp);
                    sp->connack(false, MQTT_NS::v5::connect_reason_code::success);
                    return true;
                }
            );
            ep.set_v5_disconnect_handler( // use v5 handler
                [&connections, &subs, wp]
                (MQTT_NS::v5::disconnect_reason_code reason_code, MQTT_NS::v5::properties /*props*/) {
                    locked_cout() <<
                        "[server] disconnect received." <<
                        " reason_code: " << reason_code << std::endl;
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    close_proc(connections, subs, sp);
                });
            ep.set_v5_puback_handler( // use v5 handler
                []
                (packet_id_t packet_id, MQTT_NS::v5::puback_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
                    locked_cout() <<
                        "[server] puback received. packet_id: " << packet_id <<
                        " reason_code: " << reason_code << std::endl;
                    return true;
                });
            ep.set_v5_pubrec_handler( // use v5 handler
                []
                (packet_id_t packet_id, MQTT_NS::v5::pubrec_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
                    locked_cout() <<
                        "[server] pubrec received. packet_id: " << packet_id <<
                        " reason_code: " << reason_code << std::endl;
                    return true;
                });
            ep.set_v5_pubrel_handler( // use v5 handler
                []
                (packet_id_t packet_id, MQTT_NS::v5::pubrel_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
                    locked_cout() <<
                        "[server] pubrel received. packet_id: " << packet_id <<
                        " reason_code: " << reason_code << std::endl;
                    return true;
                });
            ep.set_v5_pubcomp_handler( // use v5 handler
                []
                (packet_id_t packet_id, MQTT_NS::v5::pubcomp_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
                    locked_cout() <<
                        "[server] pubcomp received. packet_id: " << packet_id <<
                        " reason_code: " << reason_code << std::endl;
                    return true;
                });
            ep.set_v5_publish_handler( // use v5 handler
                [&ep, &subs]
                (MQTT_NS::optional<packet_id_t> packet_id,
                 MQTT_NS::publish_options pubopts,
                 MQTT_NS::buffer topic_name,
                 MQTT_NS::buffer contents,
                 MQTT_NS::v5::properties props){
                    locked_cout() << "[server] publish received."
                                  << " dup: "    << pubopts.get_dup()
                                  << " qos: "    << pubopts.get_qos()
                                  << " retain: " << pubopts.get_retain() << std::endl;
                    if (packet_id)
                        locked_cout() << "[server] packet_id: " << *packet_id << std::endl;
                    locked_cout() << "[server] topic_name: " << topic_name << std::endl;
                    locked_cout() << "[server] contents: " << contents << std::endl;
                    auto const& idx = subs.get<tag_topic>();
                    auto r = idx.equal_range(topic_name);
                    for (; r.first != r.second; ++r.first) {
                        auto retain =
                            [&] {
                                if (r.first->rap_value == MQTT_NS::rap::retain) {
                                    return pubopts.get_retain();
                                }
                                return MQTT_NS::retain::no;
                            } ();
                        r.first->con->publish(
                            topic_name,
                            contents,
                            std::min(r.first->qos_value, pubopts.get_qos()) | retain,
                            std::move(props)
                        );
                    }

                    switch (pubopts.get_qos()) {
                    case MQTT_NS::qos::at_least_once:
                        ep.puback(packet_id.get(), MQTT_NS::v5::puback_reason_code::no_matching_subscribers);
                        break;
                    case MQTT_NS::qos::exactly_once:
                        ep.pubrec(packet_id.get(), MQTT_NS::v5::pubrec_reason_code::no_matching_subscribers);
                        break;
                    default:
                        break;
                    };

                    return true;
                });
            ep.set_v5_pubrel_handler(
                [&ep]
                (packet_id_t packet_id,
                 MQTT_NS::v5::pubrel_reason_code reason_code,
                 MQTT_NS::v5::properties /*props*/) {
                    locked_cout() << "[server] pubrel received."
                                  << " packet_id: " << packet_id
                                  << " reason_code: " << reason_code << std::endl;
                    ep.pubcomp(packet_id);
                    return true;
                }
            );
            ep.set_v5_subscribe_handler( // use v5 handler
                [&subs, wp]
                (packet_id_t packet_id,
                 std::vector<std::tuple<MQTT_NS::buffer, MQTT_NS::subscribe_options>> entries,
                 MQTT_NS::v5::properties /*props*/) {
                    locked_cout() << "[server] subscribe received. packet_id: " << packet_id << std::endl;
                    std::vector<MQTT_NS::v5::suback_reason_code> res;
                    res.reserve(entries.size());
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    for (auto const& e : entries) {
                        MQTT_NS::buffer topic = std::get<0>(e);
                        MQTT_NS::qos qos_value = std::get<1>(e).get_qos();
                        MQTT_NS::rap rap_value = std::get<1>(e).get_rap();
                        locked_cout() << "[server] topic: " << topic
                                      << " qos: " << qos_value
                                      << " rap: " << rap_value
                                      << std::endl;
                        res.emplace_back(MQTT_NS::v5::qos_to_suback_reason_code(qos_value));
                        subs.emplace(std::move(topic), sp, qos_value, rap_value);
                    }
                    sp->suback(packet_id, res);
                    return true;
                }
            );
            ep.set_v5_unsubscribe_handler( // use v5 handler
                [&subs, wp]
                (packet_id_t packet_id,
                 std::vector<MQTT_NS::buffer> topics,
                 MQTT_NS::v5::properties /*props*/) {
                    locked_cout() << "[server] unsubscribe received. packet_id: " << packet_id << std::endl;
                    for (auto const& topic : topics) {
                        subs.erase(topic);
                    }
                    auto sp = wp.lock();
                    BOOST_ASSERT(sp);
                    sp->unsuback(packet_id);
                    return true;
                }
            );
        }
    );

    s.listen();
}

int main(int argc, char** argv) {
    if (argc != 2) {
        locked_cout() << argv[0] << " port" << std::endl;
        return -1;
    }

    boost::asio::io_context ioc;
    std::uint16_t port = boost::lexical_cast<std::uint16_t>(argv[1]);

    // server
    boost::asio::io_context iocs;
    auto s = MQTT_NS::server<>(
        boost::asio::ip::tcp::endpoint(
            boost::asio::ip::tcp::v4(),
            boost::lexical_cast<std::uint16_t>(argv[1])
        ),
        iocs
    );

    // You can set a specific protocol_version if you want to limit accepting version.
    // Otherwise, all protocols are accepted.
    s.set_protocol_version(MQTT_NS::protocol_version::v5);

    std::set<con_sp_t> connections;
    mi_sub_con subs;
    std::thread th(
        [&] {
            server_proc(s, connections, subs);
            iocs.run();
        }
    );


    // client
    std::uint16_t pid_sub1;
    std::uint16_t pid_sub2;

    // You can set the protocol_version to connect. If you don't set it, v3_1_1 is used.
    auto c = MQTT_NS::make_sync_client(ioc, "localhost", port, MQTT_NS::protocol_version::v5);

    int count = 0;
    auto disconnect = [&] {
        if (++count == 5) c->disconnect();
    };
    client_proc(c, pid_sub1, pid_sub2, disconnect);

    ioc.run();
    th.join();
}

The part of output:

[client] puback received. packet_id: 3 reason_code: no_matching_subscribers
[server] puback received. packet_id: 1 reason_code: success
[client] publish received.  dup: no qos: exactly_once retain: no
[client] packet_id: 2
[client] topic_name: mqtt_client_cpp/topic2_2
[client] contents: test2_2
[client] pubrec received. packet_id: 4 reason_code: no_matching_subscribers

It seems that the client receives puback/pubrec with no_matching_subscribers reason code correctly.
That means the reason code no_matching_subscribers doen't trigger 'message too long'.

"Message too long" is corresponding to oost::system::errc::message_size.

See https://wandbox.org/permlink/gZlwBzZRuJNBzo9W

I guess that there is message length related errors. The reason of the error is mqtt_cpp or mosquitto.
I think that a packet dump of the puback message helps to solve the problme.

@redboltz
Copy link
Owner

NOTE:

I use boost::system::errc::message_size for something size related errors. The output string "Message too long" is surprising for me. I might use the code for size is too short.

@shantanu1singh
Copy link
Author

Here's a TCP dump using tshark

shantanu@shsi-pc:~/repos/wireshark-3.2.5$ sudo /usr/local/bin/tshark -i lo -z follow,tcp,raw,0 
Running as user "root" and group "root". This could be dangerous.
Capturing on 'Loopback: lo'
    1 0.000000000    127.0.0.1 → 127.0.0.1    TCP 74 44322 → 1883 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 TSval=3582985956 TSecr=0 WS=128
    2 0.000013200    127.0.0.1 → 127.0.0.1    TCP 74 1883 → 44322 [SYN, ACK] Seq=0 Ack=1 Win=65483 Len=0 MSS=65495 SACK_PERM=1 TSval=3582985956 TSecr=3582985956 WS=128
    3 0.000044200    127.0.0.1 → 127.0.0.1    TCP 66 44322 → 1883 [ACK] Seq=1 Ack=1 Win=65536 Len=0 TSval=3582985956 TSecr=3582985956
    4 0.000203800    127.0.0.1 → 127.0.0.1    MQTT 99 Connect Command
    5 0.000213000    127.0.0.1 → 127.0.0.1    TCP 66 1883 → 44322 [ACK] Seq=1 Ack=34 Win=65536 Len=0 TSval=3582985956 TSecr=3582985956
    6 0.000342700    127.0.0.1 → 127.0.0.1    MQTT 74 Connect Ack
    7 0.000350200    127.0.0.1 → 127.0.0.1    TCP 66 44322 → 1883 [ACK] Seq=34 Ack=9 Win=65536 Len=0 TSval=3582985956 TSecr=3582985956
    8 0.000998200    127.0.0.1 → 127.0.0.1    MQTT 93 Subscribe Request (id=1) [autoedgehub/command]
    9 0.001040700    127.0.0.1 → 127.0.0.1    MQTT 72 Subscribe Ack (id=1)
   10 0.001069500    127.0.0.1 → 127.0.0.1    MQTT 161 Publish Message (id=2) [autoedgehub/telemetry]
   11 0.001126600    127.0.0.1 → 127.0.0.1    MQTT 71 Publish Ack (id=2)
   12 0.001220600    127.0.0.1 → 127.0.0.1    MQTT 163 Publish Message (id=3) [autoedgehub/telemetry]
   13 0.001268700    127.0.0.1 → 127.0.0.1    MQTT 71 Publish Ack (id=3)
   14 0.001466900    127.0.0.1 → 127.0.0.1    TCP 74 44324 → 1883 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 TSval=3582985957 TSecr=0 WS=128
   15 0.001475700    127.0.0.1 → 127.0.0.1    TCP 74 1883 → 44324 [SYN, ACK] Seq=0 Ack=1 Win=65483 Len=0 MSS=65495 SACK_PERM=1 TSval=3582985957 TSecr=3582985957 WS=128
   16 0.001503800    127.0.0.1 → 127.0.0.1    TCP 66 44324 → 1883 [ACK] Seq=1 Ack=1 Win=65536 Len=0 TSval=3582985958 TSecr=3582985957
   17 0.002007400    127.0.0.1 → 127.0.0.1    MQTT 106 Connect Command
   18 0.002041200    127.0.0.1 → 127.0.0.1    TCP 66 1883 → 44324 [ACK] Seq=1 Ack=41 Win=65536 Len=0 TSval=3582985958 TSecr=3582985958
   19 0.002086800    127.0.0.1 → 127.0.0.1    MQTT 70 Connect Ack
   20 0.002092400    127.0.0.1 → 127.0.0.1    TCP 66 44324 → 1883 [ACK] Seq=41 Ack=5 Win=65536 Len=0 TSval=3582985958 TSecr=3582985958
   21 0.002284400    127.0.0.1 → 127.0.0.1    MQTT 92 Subscribe Request (id=2) [sub_topic_v311/test]
   22 0.002350800    127.0.0.1 → 127.0.0.1    MQTT 71 Subscribe Ack (id=2)
   23 0.002384000    127.0.0.1 → 127.0.0.1    MQTT 100 Publish Message (id=1) [pub_topic_v311]
   24 0.002405500    127.0.0.1 → 127.0.0.1    MQTT 70 Publish Ack (id=1)
   25 0.042764897    127.0.0.1 → 127.0.0.1    TCP 66 44322 → 1883 [ACK] Seq=253 Ack=25 Win=65536 Len=0 TSval=3582985999 TSecr=3582985957
   26 0.042782997    127.0.0.1 → 127.0.0.1    TCP 66 44324 → 1883 [ACK] Seq=101 Ack=14 Win=65536 Len=0 TSval=3582985999 TSecr=3582985958
   27 1.702922465    127.0.0.1 → 127.0.0.1    MQTT 68 Disconnect Req
   28 1.703092565    127.0.0.1 → 127.0.0.1    MQTT 68 Disconnect Req
   29 1.703136765    127.0.0.1 → 127.0.0.1    TCP 66 1883 → 44322 [FIN, ACK] Seq=25 Ack=255 Win=65536 Len=0 TSval=3582987659 TSecr=3582987659
   30 1.703226965    127.0.0.1 → 127.0.0.1    TCP 66 1883 → 44324 [FIN, ACK] Seq=14 Ack=103 Win=65536 Len=0 TSval=3582987659 TSecr=3582987659
   31 1.703375165    127.0.0.1 → 127.0.0.1    TCP 66 44324 → 1883 [FIN, ACK] Seq=103 Ack=15 Win=65536 Len=0 TSval=3582987659 TSecr=3582987659
   32 1.703389865    127.0.0.1 → 127.0.0.1    TCP 66 1883 → 44324 [ACK] Seq=15 Ack=104 Win=65536 Len=0 TSval=3582987659 TSecr=3582987659
   33 1.703976465    127.0.0.1 → 127.0.0.1    TCP 66 44322 → 1883 [RST, ACK] Seq=255 Ack=26 Win=65536 Len=0 TSval=3582987660 TSecr=3582987659

The PUBACKs with ID 2 and 3 are not being received by the PUBACK handler.

Does the packet dump help debug why the parsing of the message is not working as expected?

@redboltz
Copy link
Owner

Does the packet dump help debug why the parsing of the message is not working as expected?

No, I need hex dump of the puback packet that is sent by mosquitto.

See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901121

If the packet_id is 1, reason_code is 0x10 (No matching subscribers), and no properties, then the packet should be

value meaning
0x40 PUBACK Fixed header
0x03 Remaining Length
0x00 PacketId MSB
0x01 PacketId LSB
0x10 PUBACK Reason Code (No matching subscribers)

Please show me the packet hex dump.

@shantanu1singh
Copy link
Author

All the packet details are below.
I've also attached the tcpdump file in case you'd wish to look at it using Wireshark
tcpdump - Copy.zip

image

@redboltz
Copy link
Owner

redboltz commented Jul 29, 2020

The last part of the dump(image) is 40 03 00 02 10. It is correct puback packet.
So the mosquitto seems to send the right packet.

My example code also #650 (comment) the same (packet_id is different but it is not essential) packet dump. Please double check it.

So puback doesn't seems to cause the error.

As #650 (comment) mentioned, you need to write minimal and complete code that reproduces the issue.

NOTE:
puback is processed by the function process_puback_impl() in endpoint.hpp. puback handler is called from case puback_phase::finish.

@redboltz
Copy link
Owner

mqtt_cpp works well with mosquitto 1.6.10 even if puback reason code is no_matching_subscribers.

Here is example code:

#include <iostream>
#include <iomanip>
#include <map>

#include <mqtt_client_cpp.hpp>

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cout << argv[0] << " host port" << std::endl;
        return -1;
    }

    boost::asio::io_context ioc;

    // Create no TLS client
    // You can set the protocol_version to connect. If you don't set it, v3_1_1 is used.
    auto c = MQTT_NS::make_sync_client(ioc, argv[1], argv[2], MQTT_NS::protocol_version::v5);
    using packet_id_t = typename std::remove_reference_t<decltype(*c)>::packet_id_t;

    // Setup client
    c->set_client_id("cid1");
    c->set_clean_start(true);

    // Setup handlers
    c->set_v5_connack_handler( // use v5 handler
        [&]
        (bool sp, MQTT_NS::v5::connect_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            std::cout << "[client] Connack handler called" << std::endl;
            std::cout << "[client] Session Present: " << std::boolalpha << sp << std::endl;
            std::cout << "[client] Connect Reason Code: " << reason_code << std::endl;
            if (reason_code == MQTT_NS::v5::connect_reason_code::success) {
                c->publish("mqtt_client_cpp/topic2_1", "test2_1", MQTT_NS::qos::at_least_once);
            }
            return true;
        });
    c->set_close_handler( // this handler doesn't depend on MQTT protocol version
        []
        (){
            std::cout << "[client] closed." << std::endl;
        });
    c->set_error_handler( // this handler doesn't depend on MQTT protocol version
        []
        (MQTT_NS::error_code ec){
            std::cout << "[client] error: " << ec.message() << std::endl;
        });
    c->set_v5_puback_handler( // use v5 handler
        [&]
        (packet_id_t packet_id, MQTT_NS::v5::puback_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            std::cout <<
                "[client] puback received. packet_id: " << packet_id <<
                " reason_code: " << reason_code << std::endl;
            c->disconnect();
            return true;
        });

    // Connect
    c->connect();

    ioc.run();
}

mosquitto log

1595989913: mosquitto version 1.6.10 starting
1595989913: Using default config.
1595989913: Opening ipv4 listen socket on port 1883.
1595989913: Opening ipv6 listen socket on port 1883.
1595989916: New connection from 127.0.0.1 on port 1883.
1595989916: New client connected from 127.0.0.1 as cid1 (p5, c1, k0).
1595989916: Client cid1 disconnected.

My example code log

> ./testcode 127.0.0.1 1883
[client] Connack handler called
[client] Session Present: false
[client] Connect Reason Code: success
[client] puback received. packet_id: 1 reason_code: no_matching_subscribers

@shantanu1singh
Copy link
Author

Here's the sample code I used with Mosquitto version 1.6.8

#include <iostream>
#include <iomanip>
#include <map>

#include <mqtt_client_cpp.hpp>

int main(int argc, char** argv) {
    // if (argc != 3) {
    //     std::cout << argv[0] << " host port" << std::endl;
    //     return -1;
    // }

    boost::asio::io_context ioc;

    // Create no TLS client
    // You can set the protocol_version to connect. If you don't set it, v3_1_1 is used.
    auto c = MQTT_NS::make_sync_client(ioc, "localhost", "1883", MQTT_NS::protocol_version::v5);
    using packet_id_t = typename std::remove_reference_t<decltype(*c)>::packet_id_t;

    // Setup client
    c->set_client_id("cid1");
    c->set_clean_start(true);

    // Setup handlers
    c->set_v5_connack_handler( // use v5 handler
        [&]
        (bool sp, MQTT_NS::v5::connect_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            std::cout << "[client] Connack handler called" << std::endl;
            std::cout << "[client] Session Present: " << std::boolalpha << sp << std::endl;
            std::cout << "[client] Connect Reason Code: " << reason_code << std::endl;
            if (reason_code == MQTT_NS::v5::connect_reason_code::success) {
                c->publish("mqtt_client_cpp/topic2_1", "test2_1", MQTT_NS::qos::at_least_once);
            }
            return true;
        });
    c->set_close_handler( // this handler doesn't depend on MQTT protocol version
        []
        (){
            std::cout << "[client] closed." << std::endl;
        });
    c->set_error_handler( // this handler doesn't depend on MQTT protocol version
        []
        (boost::system::error_code const &ec){
            std::cout << "[client] error: " << ec.message() << std::endl;
        });
    c->set_v5_puback_handler( // use v5 handler
        [&]
        (packet_id_t packet_id, MQTT_NS::v5::puback_reason_code reason_code, MQTT_NS::v5::properties /*props*/){
            std::cout <<
                "[client] puback received. packet_id: " << packet_id <<
                " reason_code: " << reason_code << std::endl;
            c->disconnect();
            return true;
        });

    std::cout << "Calling connect" << std::endl;

    // Connect
    c->connect();

    ioc.run();
}

Mosquitto log

1596048056: New connection from 127.0.0.1 on port 1883.
1596048056: New client connected from 127.0.0.1 as cid1 (p5, c1, k0).
1596048056: No will message specified.
1596048056: Sending CONNACK to cid1 (0, 0)
1596048056: Received PUBLISH from cid1 (d0, q1, r0, m1, 'mqtt_client_cpp/topic2_1', ... (7 bytes))
1596048056: Sending PUBACK to cid1 (m1, rc16)
1596048056: Socket error on client cid1, disconnecting.
1596048225: Received DISCONNECT from v311_client

Code log

Calling connect
[client] Connack handler called
[client] Session Present: false
[client] Connect Reason Code: success
[client] error: Message too long

@jonesmz
Copy link
Contributor

jonesmz commented Jul 29, 2020

@shantanu1singh That's very helpful.

Have you been able to catch in the debugger where this error is being generated in the mqtt_cpp code? If we knew specifically which check was failing, that would help a lot.

Any possibility of identifying the exact packet that causes the problem?

A very helpful test would be to, instead of using mosquitto, directly write the problematic packet to the socket and observe the mqtt_cpp code throwing an error.

@shantanu1singh
Copy link
Author

@jonesmz I haven't been able to do the above.

FWIW, I just tried building the sample code above with the master branch of redboltz/mqtt_cpp and the puback reason code worked. I'm going to switch to the v8.0.0 release to fix this then.

[client] Connack handler called
[client] Session Present: false
[client] Connect Reason Code: success
[client] puback received. packet_id: 1 reason_code: no_matching_subscribers
[client] closed.

So, to summarize, the problem seems to be scoped to the v7.0.1 release.

@redboltz
Copy link
Owner

redboltz commented Aug 2, 2020

Thanks you for reporting. It is fixed at v8.0.0. So I close the issue.

@redboltz redboltz closed this as completed Aug 2, 2020
redboltz added a commit that referenced this issue Sep 8, 2020
Invalid length on MQTT protocol was `boost::system::errc::message_size`.
But it was not appropriate.
See
#650 (comment)

These errors are replaced with `boost::system::errc::protocol_error`
because it is MQTT protocol violation.

Unexpected boost asio transferred_size error was also mapped
`boost::system::errc::message_size`.

It is replaced with `boost::system::errc::bad_message`.
rezahousseini pushed a commit to rezahousseini/mqtt_cpp that referenced this issue Oct 7, 2020
Invalid length on MQTT protocol was `boost::system::errc::message_size`.
But it was not appropriate.
See
redboltz#650 (comment)

These errors are replaced with `boost::system::errc::protocol_error`
because it is MQTT protocol violation.

Unexpected boost asio transferred_size error was also mapped
`boost::system::errc::message_size`.

It is replaced with `boost::system::errc::bad_message`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants