basic documentation using ChatGPT and proper backtrace

This commit is contained in:
brenodetomini
2024-04-21 17:02:18 -03:00
parent ccd31f1802
commit 2b70078e9c
50 changed files with 48264 additions and 370 deletions

View File

@@ -3,19 +3,26 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(ENV{TARGET} "x86_64-linux-gnu")
set(CMAKE_CXX_FLAGS "-Wall -fvisibility=hidden -fvisibility-inlines-hidden -Wformat -Wformat-security")
set(CMAKE_CXX_FLAGS
"-Wall -fvisibility=hidden -fvisibility-inlines-hidden -Wformat -Wformat-security -rdynamic"
)
set(CMAKE_CXX_FLAGS_DEBUG "-g2 -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-fstack-protector-all -Wl,-z,relro,-z,now -Wl,-z,noexecstack -s -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O3 -g2 -march=native -pipe -flto=auto")
set(CMAKE_CXX_FLAGS_RELEASE
"-fstack-protector-all -Wl,-z,relro,-z,now -Wl,-z,noexecstack -s -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O3 -g2 -march=native -pipe -flto=auto"
)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
enable_testing()
execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/version.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE MQTTD_VERSION)
execute_process(
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/version.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE MQTTD_VERSION)
project(mqttd VERSION ${MQTTD_VERSION} LANGUAGES CXX)
project(
mqttd
VERSION ${MQTTD_VERSION}
LANGUAGES CXX)
add_subdirectory(src)
add_subdirectory(tests)

View File

@@ -6,7 +6,7 @@
FindAutotools
-------------
The main purpose is to detect that commonly used Autotools programs are isntalled.
The main purpose is to detect that commonly used Autotools programs are installed.
This lets the user know at CMake configuration time if the computer is ready to
build an autotools-based ExternalProject.

14
cmake/GetCppTrace.cmake Normal file
View File

@@ -0,0 +1,14 @@
include(FetchContent)
set(CPPTRACE_UNWIND_WITH_UNWIND Off)
set(CPPTRACE_UNWIND_WITH_LIBUNWIND On)
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF Off)
set(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE On)
set(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH On)
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v0.5.2 # <HASH or TAG>
GIT_PROGRESS TRUE
OVERRIDE_FIND_PACKAGE)
FetchContent_MakeAvailable(cpptrace)

View File

@@ -30,10 +30,16 @@ set(DOXYGEN_SORT_BY_SCOPE_NAME YES)
set(DOXYGEN_SORT_MEMBER_DOCS NO)
set(DOXYGEN_SOURCE_BROWSER YES)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md")
set(DOXYGEN_EXTRA_FILES "${CMAKE_SOURCE_DIR}/docs/mqtt_spec/")
doxygen_add_docs(doc
"${CMAKE_SOURCE_DIR}/src"
"${CMAKE_SOURCE_DIR}/tests"
ALL
COMMENT "Generate HTML documentation")
doxygen_add_docs(
doc "${CMAKE_SOURCE_DIR}/src" "${CMAKE_SOURCE_DIR}/tests"
"${CMAKE_SOURCE_DIR}/README.md" ALL COMMENT "Generate HTML documentation")
add_custom_target(
open_docs
COMMAND ${CMAKE_COMMAND} -E env xdg-open
${CMAKE_SOURCE_DIR}/build/docs/html/index.html
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build/docs/html
COMMENT "Opening Doxygen documentation")

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because it is too large Load Diff

4
scripts/crashlog.txt Normal file
View File

@@ -0,0 +1,4 @@
#1 0x4025af sp=0x7fff3ff7c270 <unknown> + 0x0
#2 0x7f0b9234114a sp=0x7fff3ff7c290 __libc_start_call_main + 0x7a
#3 0x7f0b9234120b sp=0x7fff3ff7c330 __libc_start_main + 0x8b
#4 0x4025e5 sp=0x7fff3ff7c390 <unknown> + 0x8b

BIN
scripts/mqttd Executable file

Binary file not shown.

53
scripts/process_crashlog.py Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
from pathlib import Path
import shutil
import subprocess
class Crashlog():
def __init__(self, crashlog: Path, executable: Path):
self.executable = executable
self.addresses = self.read_crashlog(crashlog)
self.translated_addresses = self.translate_addresses(self.addresses, executable)
def read_crashlog(self, crashlog: Path) -> list[str]:
_addresses = []
try:
with open(crashlog, "r") as f:
for line in f:
if len(line.strip()) <= 0:
continue
tokens = line.split()
_addresses.append(tokens[1])
except FileNotFoundError:
print("O arquivo não foi encontrado.")
except Exception as e:
print(f"Ocorreu um error: {e}")
finally:
return _addresses
def translate_addresses(self, addresses: list[str], executable: Path) -> dict[str, str]:
_translated_addr = {}
if shutil.which("addr2line") is None:
raise RuntimeError("addr2line binary not found")
for addr in addresses:
_path = executable.absolute()
_cmd = f"addr2line --functions --pretty-print --demangle --basenames --exe {_path} {addr}"
_process = subprocess.run(_cmd, shell=True, check=True, capture_output=True)
_translated_addr[addr] = _process.stdout
return _translated_addr
def __str__(self):
_stdout = ""
for key, value in self.translated_addresses.items():
_stdout += value.decode()
return _stdout
if __name__ == "__main__":
print(Crashlog(Path("crashlog.txt"), Path("mqttd")))
pass

7
setup.cfg Normal file
View File

@@ -0,0 +1,7 @@
[isort]
profile = black
[pycodestyle]
max-line-length = 128
ignore = E203,E701, E402, E501, E722

View File

@@ -1,8 +1,13 @@
include(GetSpdlog)
include(GetCppTrace)
add_subdirectory(libmqttd)
add_subdirectory(core)
add_executable(mqttd mqttd.cpp)
target_link_libraries(mqttd PRIVATE libcore libmqttd spdlog)
target_link_libraries(mqttd PRIVATE libcore libmqttd spdlog cpptrace::cpptrace)
target_include_directories(mqttd PUBLIC "${PROJECT_BINARY_DIR}")
add_executable(mqttd_stacktracer mqttd_stacktracer.cpp)
target_link_libraries(mqttd_stacktracer PRIVATE cpptrace::cpptrace)
target_include_directories(mqttd_stacktracer PUBLIC "${PROJECT_BINARY_DIR}")

View File

@@ -1,6 +1,5 @@
add_library(libcore "")
add_subdirectory(backtrace)
add_subdirectory(broker)
FILE(GLOB CPP_FILES CONFIGURE_DEPENDS *.cpp)

View File

@@ -1,14 +0,0 @@
FILE(GLOB CPP_FILES CONFIGURE_DEPENDS *.cpp)
FILE(GLOB HPP_FILES CONFIGURE_DEPENDS *.hpp)
include(GetUnwind)
target_sources(libcore
PRIVATE ${CPP_FILES}
PUBLIC ${HPP_FILES}
)
target_link_libraries(libcore PRIVATE libunwind::static)
target_include_directories(libcore PUBLIC $<TARGET_PROPERTY:libunwind::static,INTERFACE_INCLUDE_DIRECTORIES> ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -1,34 +0,0 @@
#include "backtrace.hpp"
void backtrace::backtrace() {
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
int n = 0;
while (unw_step(&cursor)) {
unw_word_t ip, sp, off;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
char symbol[256] = {"<unknown>"};
char *name = symbol;
if (!unw_get_proc_name(&cursor, symbol, sizeof(symbol), &off)) {
int status;
if ((name = abi::__cxa_demangle(symbol, NULL, NULL, &status)) == 0)
name = symbol;
}
printf("#%-2d 0x%016" PRIxPTR " sp=0x%016" PRIxPTR " %s + 0x%" PRIxPTR "\n",
++n, static_cast<uintptr_t>(ip), static_cast<uintptr_t>(sp), name,
static_cast<uintptr_t>(off));
if (name != symbol)
free(name);
}
}

View File

@@ -1,22 +0,0 @@
#ifndef BACKTRACE_H
#define BACKTRACE_H
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <cxxabi.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
namespace backtrace {
void backtrace();
}
#endif /* end of include guard: BACKTRACE_H */

View File

@@ -1,9 +1,6 @@
#include "disconnect_packet.hpp"
#include "session.hpp"
#include "session_manager.hpp"
#include <algorithm>
#include <connection_listener.hpp>
#include <memory>
#include <csignal>
#include <thread>
ConnectionListener::ConnectionListener(int port) { this->connection_listener_port = port; }
@@ -24,8 +21,7 @@ void ConnectionListener::join() {
this->listener.join();
}
void ConnectionListener::create_new_session(int socket_fd) {
}
void ConnectionListener::create_new_session(int socket_fd) {}
void ConnectionListener::listen() {
struct sockaddr_in serverAddr;

View File

@@ -3,33 +3,62 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
#include <thread>
#include <unistd.h>
#include <spdlog/spdlog.h>
#include <exit_codes.hpp>
#include <session.hpp>
#include <spdlog/spdlog.h>
class ConnectionListener{
/**
* @brief Class responsible for listening to incoming connections.
*/
class ConnectionListener {
public:
/**
* @brief Constructs a ConnectionListener object with the specified port.
*
* @param port The port to listen for incoming connections (default is 1883).
*/
ConnectionListener(int port = 1883);
/**
* @brief Destroys the ConnectionListener object.
*/
~ConnectionListener();
/**
* @brief Starts listening for incoming connections.
*/
void start();
/**
* @brief Stops listening for incoming connections.
*/
void stop();
/**
* @brief Joins the listener thread.
*/
void join();
private:
bool is_listening;
int connection_listener_socket;
int connection_listener_port;
std::thread listener;
bool is_listening; /**< Flag indicating whether the listener is currently active. */
int connection_listener_socket; /**< Socket file descriptor for the listener. */
int connection_listener_port; /**< Port number to listen on. */
std::thread listener; /**< Listener thread responsible for accepting incoming connections. */
/**
* @brief Creates a new session for the accepted connection.
*
* @param socket_fd The socket file descriptor of the accepted connection.
*/
static void create_new_session(int socket_fd);
// std::vector<Session> current_sessions;
/**
* @brief Listens for incoming connections.
*/
void listen();
};

View File

@@ -6,34 +6,91 @@
#include <string>
#include <unordered_map>
/**
* @brief Class responsible for managing MQTT sessions.
*/
class SessionManager {
public:
SessionManager(const SessionManager&) = delete;
SessionManager operator=(const SessionManager&) = delete;
/**
* @brief Deleted copy constructor.
*/
SessionManager(const SessionManager &) = delete;
/**
* @brief Deleted copy assignment operator.
*/
SessionManager operator=(const SessionManager &) = delete;
/**
* @brief Retrieves the singleton instance of SessionManager.
*
* @return Reference to the singleton instance of SessionManager.
*/
static SessionManager &get_instance();
/**
* @brief Destroys the SessionManager object.
*/
~SessionManager();
/**
* @brief Creates a new session for the specified socket file descriptor.
*
* @param socket_fd The socket file descriptor for the new session.
*/
void new_session(int socket_fd);
/**
* @brief Shuts down the session with the given client ID.
*
* @param client_id The ID of the session to shut down.
*/
void shutdown_session(const std::string &client_id);
/**
* @brief Shuts down all active sessions.
*/
void shutdown_all_sessions();
/**
* @brief Prints information about all active sessions.
*/
void print_sessions();
private:
static SessionManager *singleton;
SessionManager() {};
static SessionManager *singleton; /**< Pointer to the singleton instance of SessionManager. */
SessionManager(){}; /**< Private constructor to prevent instantiation. */
/**
* @brief Creates a new session for the specified socket file descriptor.
*
* @param socket_fd The socket file descriptor for the new session.
*/
void create_session(int socket_fd);
void add_session_to_pool(Session *);
void remove_session_from_pool(Session *);
void check_for_session_takeover(const std::string&);
/**
* @brief Adds a session to the pool of connected sessions.
*
* @param session Pointer to the session to add.
*/
void add_session_to_pool(Session *);
/**
* @brief Removes a session from the pool of connected sessions.
*
* @param session Pointer to the session to remove.
*/
void remove_session_from_pool(Session *);
// TODO: Session* must be a unique_ptr or is this fine?
std::unordered_map<std::string, Session *> connected_sessions;
mutable std::shared_mutex connected_sessions_lock;
/**
* @brief Checks for session takeover based on the client ID.
*
* @param client_id The ID of the session to check.
*/
void check_for_session_takeover(const std::string &);
std::unordered_map<std::string, Session *> connected_sessions; /**< Map of client IDs to session pointers. */
mutable std::shared_mutex connected_sessions_lock; /**< Mutex for thread-safe access to connected_sessions. */
};
#endif // INCLUDE_BROKER_SESSION_MANAGER_HPP_

View File

@@ -3,14 +3,32 @@
#include <stdexcept>
/**
* @brief Exception thrown when a function is not yet implemented.
*/
class NotImplemented : public std::logic_error {
public:
/**
* @brief Constructs a NotImplemented exception.
*/
NotImplemented() : std::logic_error("Function not yet implemented"){};
};
/**
* @brief Exception thrown when a received packet is malformed.
*/
class MalformedPacket : public std::runtime_error {
public:
/**
* @brief Constructs a MalformedPacket exception.
*/
MalformedPacket() : std::runtime_error("Received a malformed packet"){};
/**
* @brief Constructs a MalformedPacket exception with additional information.
*
* @param info Additional information about the malformed packet.
*/
MalformedPacket(std::string info) : std::runtime_error("Received a malformed packet: " + info) {}
};
#endif // INCLUDE_LIBMQTTD_EXCEPTIONS_HPP_

View File

@@ -2,21 +2,20 @@
#include "packet_interface.hpp"
#include "property.hpp"
#include <connect_ack.hpp>
#include <csignal>
#include <cstddef>
#include <vector>
ConnectACK::ConnectACK() : PacketInterface() {
PacketInterface::fixed_header.packet_type = PacketType::CONNACK;
PacketInterface::fixed_header.packet_flags = 0;
ConnectACK::ConnectACK() : IPacket() {
IPacket::fixed_header.packet_type = PacketType::CONNACK;
IPacket::fixed_header.packet_flags = 0;
// There is a obrigatory reason code and connect acknowledge flags after fixed header, thus we are garanted to have at least 2 bytes
PacketInterface::fixed_header.remaining_length = 2;
IPacket::fixed_header.remaining_length = 2;
this->reason_code = ConnectReasonCode::SUCCESS;
this->session_present = false;
};
void ConnectACK::add_property(const PropertyIdentifier &prop, const PropertyValue &value) {
this->properties[prop] = value;
}
void ConnectACK::add_property(const PropertyIdentifier &prop, const PropertyValue &value) { this->properties[prop] = value; }
std::vector<std::byte> ConnectACK::as_bytes() {
std::vector<std::byte> variable_header_bytes;
@@ -30,9 +29,9 @@ std::vector<std::byte> ConnectACK::as_bytes() {
std::vector<std::byte> properties_bytes = properties.as_bytes();
variable_header_bytes.insert(variable_header_bytes.end(), properties_bytes.begin(), properties_bytes.end());
PacketInterface::fixed_header.remaining_length = variable_header_bytes.size();
IPacket::fixed_header.remaining_length = variable_header_bytes.size();
std::vector<std::byte> fixed_header_bytes = PacketInterface::as_bytes();
std::vector<std::byte> fixed_header_bytes = IPacket::as_bytes();
fixed_header_bytes.insert(fixed_header_bytes.end(), variable_header_bytes.begin(), variable_header_bytes.end());
return fixed_header_bytes;
}

View File

@@ -6,82 +6,82 @@
/**
* @brief Enum class representing the reason codes for MQTT Connect packets according to MQTT v5.0 specification.
*
*
* These reason codes are used to indicate the result of a connection attempt.
* Refer to <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901079">MQTT Version 5.0 topic 3.2.2.2</a> for more information
*/
enum class ConnectReasonCode : uint8_t {
SUCCESS = 0, /**< The Connection is accepted. */
UNSPECIFIED_ERROR = 128, /**< The Server does not wish to reveal the reason for the failure, or none of the other Reason Codes apply. */
MALFORMED_PACKET = 129, /**< Data within the CONNECT packet could not be correctly parsed. */
PROTOCOL_ERROR = 130, /**< Data in the CONNECT packet does not conform to this specification. */
SUCCESS = 0, /**< The Connection is accepted. */
UNSPECIFIED_ERROR = 128, /**< The Server does not wish to reveal the reason for the failure, or none of the other Reason Codes apply. */
MALFORMED_PACKET = 129, /**< Data within the CONNECT packet could not be correctly parsed. */
PROTOCOL_ERROR = 130, /**< Data in the CONNECT packet does not conform to this specification. */
IMPLEMENTATION_SPECIFIC_ERROR = 131, /**< The CONNECT is valid but is not accepted by this Server. */
UNSUPPORTED_PROTOCOL_VERSION = 132, /**< The Server does not support the version of the MQTT protocol requested by the Client. */
CLIENT_IDENTIFIER_NOT_VALID = 133, /**< The Client Identifier is a valid string but is not allowed by the Server. */
BAD_USERNAME_OR_PASSWORD = 134, /**< The provided username or password is not valid. */
NOT_AUTHORIZED = 135, /**< The Client is not authorized to connect. */
SERVER_UNAVAILABLE = 136, /**< The MQTT Server is not available. */
SERVER_BUSY = 137, /**< The Server is busy. Try again later. */
BANNED = 138, /**< This Client has been banned by administrative action. */
BAD_AUTHENTICATION_METHOD = 140, /**< The authentication method is not supported or does not match the authentication method currently in use. */
TOPIC_NAME_INVALID = 144, /**< The Will Topic Name is not malformed, but is not accepted by this Server. */
PACKET_TOO_LARGE = 149, /**< The CONNECT packet exceeded the maximum permissible size. */
QUOTA_EXCEEDED = 151, /**< An implementation or administrative imposed limit has been exceeded. */
PAYLOAD_FORMAT_INVALID = 153, /**< The Will Payload does not match the specified Payload Format Indicator. */
RETAIN_NOT_SUPPORTED = 154, /**< The Server does not support retained messages, and Will Retain was set to 1. */
QOS_NOT_SUPPORTED = 155, /**< The Server does not support the QoS set in Will QoS. */
USER_ANOTHER_SERVER = 156, /**< The Client should temporarily use another server. */
SERVER_MOVED = 157, /**< The Client should permanently use another server. */
CONNECTION_RATE_EXCEEDED = 159 /**< The connection rate limit has been exceeded. */
UNSUPPORTED_PROTOCOL_VERSION = 132, /**< The Server does not support the version of the MQTT protocol requested by the Client. */
CLIENT_IDENTIFIER_NOT_VALID = 133, /**< The Client Identifier is a valid string but is not allowed by the Server. */
BAD_USERNAME_OR_PASSWORD = 134, /**< The provided username or password is not valid. */
NOT_AUTHORIZED = 135, /**< The Client is not authorized to connect. */
SERVER_UNAVAILABLE = 136, /**< The MQTT Server is not available. */
SERVER_BUSY = 137, /**< The Server is busy. Try again later. */
BANNED = 138, /**< This Client has been banned by administrative action. */
BAD_AUTHENTICATION_METHOD = 140, /**< The authentication method is not supported or does not match the authentication method currently in use. */
TOPIC_NAME_INVALID = 144, /**< The Will Topic Name is not malformed, but is not accepted by this Server. */
PACKET_TOO_LARGE = 149, /**< The CONNECT packet exceeded the maximum permissible size. */
QUOTA_EXCEEDED = 151, /**< An implementation or administrative imposed limit has been exceeded. */
PAYLOAD_FORMAT_INVALID = 153, /**< The Will Payload does not match the specified Payload Format Indicator. */
RETAIN_NOT_SUPPORTED = 154, /**< The Server does not support retained messages, and Will Retain was set to 1. */
QOS_NOT_SUPPORTED = 155, /**< The Server does not support the QoS set in Will QoS. */
USER_ANOTHER_SERVER = 156, /**< The Client should temporarily use another server. */
SERVER_MOVED = 157, /**< The Client should permanently use another server. */
CONNECTION_RATE_EXCEEDED = 159 /**< The connection rate limit has been exceeded. */
};
/**
/**
* @brief Connect Acknowledge packet according to <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074">MQTT Version 5.0 topic 3.2</a>.
*
*
* This class represents a Connect Acknowledge packet as defined in the MQTT Version 5.0 specification (topic 3.2).
*/
class ConnectACK : public PacketInterface {
class ConnectACK : public IPacket {
public:
/**
* @brief Constructs a new Connect ACK packet with default values.
*
*
* The default values are: reason code set to SUCCESS (0x00) and session_present set to false.
*/
ConnectACK();
~ConnectACK() = default;
/**
* @brief Sets the session_present value.
*
*
* @param value The value to set for session_present.
*/
void set_session_present(const bool &value) { this->session_present = value; };
/**
* @brief Sets the reason code to be returned to the client.
*
*
* @param value The ConnectReasonCode to set as the reason code.
*/
void set_reason_code(const ConnectReasonCode &value) { this->reason_code = value; };
/**
* @brief Adds a property to the property map.
*
*
* @param identifier The identifier of the property to add.
* @param value The value of the property to add.
*/
void add_property(const PropertyIdentifier& identifier, const PropertyValue& value);
void add_property(const PropertyIdentifier &identifier, const PropertyValue &value);
/**
* @brief Converts the packet to bytes for transmission over a socket to the client.
*
*
* @return A vector of bytes representing the packet.
*/
std::vector<std::byte> as_bytes();
private:
//Variable Header
// Variable Header
/**
* @brief Indicates whether the Server is using Session State from a previous connection for the respective ClientID.
*/
@@ -89,7 +89,7 @@ private:
/**
* @brief The reason code indicating if the connection was accepted or not.
*
*
* Only ConnectReasonCode::SUCCESS indicates a successful connection.
*/
ConnectReasonCode reason_code;
@@ -100,4 +100,4 @@ private:
MQTTProperties properties;
};
#endif // INCLUDE_CONNECTION_CONNECT_ACK_HPP_
#endif // INCLUDE_CONNECTION_CONNECT_ACK_HPP_

View File

@@ -8,14 +8,14 @@
#include <sstream>
#include <stdexcept>
ConnectPacket::ConnectPacket(PacketInterface &packet) : PacketInterface(packet) {
ConnectPacket::ConnectPacket(IPacket &packet) : IPacket(packet) {
parse_variable_header();
parse_payload();
}
ConnectPacket::~ConnectPacket() {}
ConnectPacket::ConnectPacket(std::vector<std::byte> data) : PacketInterface(data) { parse_variable_header(); }
ConnectPacket::ConnectPacket(std::vector<std::byte> data) : IPacket(data) { parse_variable_header(); }
void ConnectPacket::parse_variable_header() {
spdlog::trace("Parsing Connect variable header");
@@ -55,7 +55,7 @@ ConnectionFlagValue ConnectPacket::get_connection_flag(ConnectFlags flag) const
std::string ConnectPacket::as_string() const {
std::ostringstream packet_str;
packet_str << PacketInterface::as_string();
packet_str << IPacket::as_string();
packet_str << "\t"
<< "\u21B3"
<< "Variable Hedaer" << std::endl;

View File

@@ -22,9 +22,9 @@ enum ConnectFlags : uint16_t {
using ConnectionFlagValue = std::variant<bool, uint8_t>;
class ConnectPacket : public PacketInterface {
class ConnectPacket : public IPacket {
public:
ConnectPacket(PacketInterface &);
ConnectPacket(IPacket &);
ConnectPacket(std::vector<std::byte>);
~ConnectPacket();
@@ -47,7 +47,7 @@ private:
std::vector<std::byte>::const_iterator payload_start_byte;
uint16_t packet_identifier = 0;
MQTTProperties properties;
uint byte_size = 0;
uint size_in_bytes = 0;
// Payload
utf8_str client_id;

View File

@@ -6,27 +6,27 @@
#include <disconnect_packet.hpp>
#include <vector>
DisconnectPacket::DisconnectPacket() : PacketInterface() {
PacketInterface::fixed_header.packet_type = PacketType::DISCONNECT;
DisconnectPacket::DisconnectPacket() : IPacket() {
IPacket::fixed_header.packet_type = PacketType::DISCONNECT;
// There is a obrigatory reason code
PacketInterface::fixed_header.remaining_length = 1;
IPacket::fixed_header.remaining_length = 1;
this->reason_code = DisconnectReasonCode::UNSPECIFIED_ERROR;
};
DisconnectPacket::DisconnectPacket(PacketInterface &packet) : PacketInterface(packet) {
DisconnectPacket::DisconnectPacket(IPacket &packet) : IPacket(packet) {
// There is a obrigatory reason code
PacketInterface::fixed_header.remaining_length = 1;
IPacket::fixed_header.remaining_length = 1;
uint8_t reason_code = utilities::types::get_fixed_size_integer<uint8_t>(PacketInterface::variable_header_start_byte);
uint8_t reason_code = utilities::types::get_fixed_size_integer<uint8_t>(IPacket::variable_header_start_byte);
this->reason_code = DisconnectReasonCode(reason_code);
auto proprties_start_byte = std::next(PacketInterface::variable_header_start_byte);
auto proprties_start_byte = std::next(IPacket::variable_header_start_byte);
this->properties = MQTTProperties(proprties_start_byte);
}
DisconnectPacket::DisconnectPacket(const std::vector<std::byte> &data) : PacketInterface(data) {
uint8_t reason_code = utilities::types::get_fixed_size_integer<uint8_t>(PacketInterface::variable_header_start_byte);
DisconnectPacket::DisconnectPacket(const std::vector<std::byte> &data) : IPacket(data) {
uint8_t reason_code = utilities::types::get_fixed_size_integer<uint8_t>(IPacket::variable_header_start_byte);
this->reason_code = DisconnectReasonCode(reason_code);
auto proprties_start_byte = std::next(PacketInterface::variable_header_start_byte);
auto proprties_start_byte = std::next(IPacket::variable_header_start_byte);
this->properties = MQTTProperties(proprties_start_byte);
}
@@ -39,9 +39,9 @@ std::vector<std::byte> DisconnectPacket::as_bytes() {
std::vector<std::byte> properties_bytes = properties.as_bytes();
variable_header_bytes.insert(variable_header_bytes.end(), properties_bytes.begin(), properties_bytes.end());
PacketInterface::fixed_header.remaining_length = variable_header_bytes.size();
IPacket::fixed_header.remaining_length = variable_header_bytes.size();
std::vector<std::byte> fixed_header_bytes = PacketInterface::as_bytes();
std::vector<std::byte> fixed_header_bytes = IPacket::as_bytes();
fixed_header_bytes.insert(fixed_header_bytes.end(), variable_header_bytes.begin(), variable_header_bytes.end());
return fixed_header_bytes;
}

View File

@@ -4,55 +4,94 @@
#include <packet_interface.hpp>
#include <property.hpp>
/**
* @brief Enum representing the reason codes for disconnecting from MQTT.
*/
enum class DisconnectReasonCode : uint8_t {
NORMAL_DISCONNECTION = 0,
DISCONNECT_WITH_WILL_MESSAGE = 4,
UNSPECIFIED_ERROR = 128,
MALFORMED_PACKET = 129,
PROTOCOL_ERROR = 130,
IMPLEMENTATION_SPECIFIC_ERROR = 131,
NOT_AUTHORIZED = 135,
SERVER_BUSY = 137,
SERVER_SHUTING_DOWN = 139,
KEEPALIVE_TIMEOUT = 141,
SESSION_TAKEN_OVER = 142,
TOPIC_FILTER_INVALID = 143,
TOPIC_NAME_INVALID = 144,
RECEIVE_MAXIMUM_EXCEEDED = 147,
TOPIC_ALIAS_INVALID = 148,
PACKET_TOO_LARGE = 149,
MESSAGE_RATE_TOO_HIGH = 150,
QUOTA_EXCEEDED = 151,
ADMINISTRATIVE_ACTION = 152,
PAYLOAD_FORMAT_INVALID = 153,
RETAIN_NOT_SUPPORTED = 154,
QOS_NOT_SUPPORTED = 155,
USER_ANOTHER_SERVER = 156,
SERVER_MOVED = 157,
SHARED_SUBSCRIPTION_NOT_SUPPORTED = 158,
CONNECTION_RATE_EXCEEDED = 159,
MAXIMUM_CONNECT_TIMEOUT = 160,
SUBSCRIPTIONS_IDENTIFIERS_NOT_SUPPORTED = 161,
WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162,
NORMAL_DISCONNECTION = 0, /**< Close the connection normally. Do not send the Will Message. */
DISCONNECT_WITH_WILL_MESSAGE = 4, /**< The Client wishes to disconnect but requires that the Server also publishes its Will Message. */
UNSPECIFIED_ERROR = 128, /**< The Connection is closed but the sender either does not wish to reveal the reason, or none of the other Reason Codes apply. */
MALFORMED_PACKET = 129, /**< The received packet does not conform to this specification. */
PROTOCOL_ERROR = 130, /**< An unexpected or out of order packet was received. */
IMPLEMENTATION_SPECIFIC_ERROR = 131, /**< The packet received is valid but cannot be processed by this implementation. */
NOT_AUTHORIZED = 135, /**< The request is not authorized. */
SERVER_BUSY = 137, /**< The Server is busy and cannot continue processing requests from this Client. */
SERVER_SHUTING_DOWN = 139, /**< The Server is shutting down. */
KEEPALIVE_TIMEOUT = 141, /**< The Connection is closed because no packet has been received for 1.5 times the Keepalive time. */
SESSION_TAKEN_OVER = 142, /**< Another Connection using the same ClientID has connected causing this Connection to be closed. */
TOPIC_FILTER_INVALID = 143, /**< The Topic Filter is correctly formed, but is not accepted by this Sever. */
TOPIC_NAME_INVALID = 144, /**< The Topic Name is correctly formed, but is not accepted by this Client or Server. */
RECEIVE_MAXIMUM_EXCEEDED = 147, /**< The Client or Server has received more than Receive Maximum publication for which it has not sent PUBACK or PUBCOMP. */
TOPIC_ALIAS_INVALID = 148, /**< The Client or Server has received a PUBLISH packet containing a Topic Alias which is greater than the Maximum Topic Alias it sent in the CONNECT or CONNACK packet. */
PACKET_TOO_LARGE = 149, /**< The packet size is greater than Maximum Packet Size for this Client or Server. */
MESSAGE_RATE_TOO_HIGH = 150, /**< Message rate too high. */
QUOTA_EXCEEDED = 151, /**< An implementation or administrative imposed limit has been exceeded. */
ADMINISTRATIVE_ACTION = 152, /**< The Connection is closed due to an administrative action. */
PAYLOAD_FORMAT_INVALID = 153, /**< The payload format does not match the one specified by the Payload Format Indicator. */
RETAIN_NOT_SUPPORTED = 154, /**< The Server has does not support retained messages. */
QOS_NOT_SUPPORTED = 155, /**< The Client specified a QoS greater than the QoS specified in a Maximum QoS in the CONNACK. */
USER_ANOTHER_SERVER = 156, /**< The Client should temporarily change its Server. */
SERVER_MOVED = 157, /**< The Server is moved and the Client should permanently change its server location. */
SHARED_SUBSCRIPTION_NOT_SUPPORTED = 158, /**< The Server does not support Shared Subscriptions. */
CONNECTION_RATE_EXCEEDED = 159, /**< This connection is closed because the connection rate is too high. */
MAXIMUM_CONNECT_TIMEOUT = 160, /**< This connection is closed because the connection rate is too high. */
SUBSCRIPTIONS_IDENTIFIERS_NOT_SUPPORTED = 161, /**< The Server does not support Subscription Identifiers; the subscription is not accepted. */
WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162, /**< The Server does not support Wildcard Subscriptions; the subscription is not accepted. */
};
std::ostream& operator<<(std::ostream& os, DisconnectReasonCode code);
/**
* @brief Overload of the output operator to print the DisconnectReasonCode.
*/
std::ostream &operator<<(std::ostream &os, DisconnectReasonCode code);
class DisconnectPacket : public PacketInterface {
/**
* @brief Class representing a Disconnect Packet in MQTT communication.
*/
class DisconnectPacket : public IPacket {
public:
/**
* @brief Default constructor.
*/
DisconnectPacket();
DisconnectPacket(PacketInterface &);
/**
* @brief Copy constructor.
*
* @param other The DisconnectPacket object to copy.
*/
DisconnectPacket(IPacket &other);
/**
* @brief Constructs a DisconnectPacket object from byte vector data.
*
* @param data The byte vector containing the packet data.
*/
DisconnectPacket(const std::vector<std::byte> &data);
/**
* @brief Destructor.
*/
~DisconnectPacket() = default;
/**
* @brief Adds a property to the Disconnect Packet.
*
* @param prop The property identifier.
* @param value The property value.
*/
void add_property(const PropertyIdentifier &prop, const PropertyValue &value);
DisconnectReasonCode reason_code;
DisconnectReasonCode reason_code; /**< The reason code for disconnecting. */
/**
* @brief Converts the Disconnect Packet to a byte vector.
*
* @return The byte vector representation of the Disconnect Packet.
*/
std::vector<std::byte> as_bytes();
private:
// Variable Header
MQTTProperties properties;
MQTTProperties properties; /**< The properties of the Disconnect Packet. */
};
#endif // INCLUDE_DISCONNECTION_DISCONNECT_PACKET_HPP_

View File

@@ -5,33 +5,41 @@
#include <bitset>
#include <ostream>
/**
* @brief Enumeration of MQTT packet types.
*/
enum class PacketType : uint8_t {
RESERVED = 0,
CONNECT = 1,
CONNACK = 2,
PUBLISH = 3,
PUBACK = 4,
PUBREC = 5,
PUBREL = 6,
PUBCOMP = 7,
SUBSCRIBE = 8,
SUBACK = 9,
UNSUBSCRIBE = 10,
UNSUBACK = 11,
PINGREQ = 12,
PINGRESP = 13,
DISCONNECT = 14,
AUTH = 15,
UNSET=99,
RESERVED = 0, /**< Reserved. */
CONNECT = 1, /**< CONNECT: Client request to connect to Server. */
CONNACK = 2, /**< CONNACK: Connect acknowledgment. */
PUBLISH = 3, /**< PUBLISH: Publish message. */
PUBACK = 4, /**< PUBACK: Publish acknowledgment. */
PUBREC = 5, /**< PUBREC: Publish received (assured delivery part 1). */
PUBREL = 6, /**< PUBREL: Publish release (assured delivery part 2). */
PUBCOMP = 7, /**< PUBCOMP: Publish complete (assured delivery part 3). */
SUBSCRIBE = 8, /**< SUBSCRIBE: Client subscribe request. */
SUBACK = 9, /**< SUBACK: Subscribe acknowledgment. */
UNSUBSCRIBE = 10, /**< UNSUBSCRIBE: Unsubscribe request. */
UNSUBACK = 11, /**< UNSUBACK: Unsubscribe acknowledgment. */
PINGREQ = 12, /**< PINGREQ: PING request. */
PINGRESP = 13, /**< PINGRESP: PING response. */
DISCONNECT = 14, /**< DISCONNECT: Client is disconnecting. */
AUTH = 15, /**< AUTH: Authentication exchange. */
UNSET = 99, /**< Unset or unknown type. */
};
std::ostream& operator<<(std::ostream& lhs, PacketType p);
/**
* @brief Overload of the output operator to print the packet type.
*/
std::ostream &operator<<(std::ostream &lhs, PacketType p);
/**
* @brief Structure representing the fixed header of an MQTT packet.
*/
struct FixedHeader {
PacketType packet_type = PacketType(0);
std::bitset<4> packet_flags = {0};
int_vb remaining_length = int_vb(0);
PacketType packet_type = PacketType(0); /**< Packet type. */
std::bitset<4> packet_flags = {0}; /**< Packet flags. */
int_vb remaining_length = int_vb(0); /**< Remaining length. */
};
#endif // INCLUDE_PACKET_INTERFACE_FIXED_HEADER_HPP_

View File

@@ -3,13 +3,13 @@
#include <vector>
#include <sstream>
PacketInterface::PacketInterface() {
IPacket::IPacket() {
this->raw_data = {};
this->fixed_header.packet_type = PacketType::UNSET;
this->fixed_header.remaining_length = 0;
}
PacketInterface::PacketInterface(const std::vector<std::byte> &data) {
IPacket::IPacket(const std::vector<std::byte> &data) {
this->raw_data = data;
unsigned char fixed_header_packet_type = (static_cast<unsigned char>(raw_data[0]) >> 4) & 0x0F;
this->fixed_header.packet_type = PacketType(fixed_header_packet_type);
@@ -22,7 +22,7 @@ PacketInterface::PacketInterface(const std::vector<std::byte> &data) {
this->variable_header_start_byte = std::next(raw_data.begin(), fixed_header.remaining_length.size() + 1);
}
PacketInterface::PacketInterface(const PacketInterface &packet) {
IPacket::IPacket(const IPacket &packet) {
this->fixed_header.packet_type = packet.fixed_header.packet_type;
this->fixed_header.packet_flags = packet.fixed_header.packet_flags;
this->fixed_header.remaining_length = packet.fixed_header.remaining_length;
@@ -31,7 +31,7 @@ PacketInterface::PacketInterface(const PacketInterface &packet) {
this->raw_data = packet.raw_data;
}
std::string PacketInterface::as_string() const {
std::string IPacket::as_string() const {
std::ostringstream packet_str;
packet_str << std::endl;
packet_str << "Packet Type = " << this->get_packet_type() << std::endl;
@@ -45,7 +45,7 @@ std::string PacketInterface::as_string() const {
return packet_str.str();
}
std::vector<std::byte> PacketInterface::as_bytes() {
std::vector<std::byte> IPacket::as_bytes() {
std::vector<std::byte> packet_bytes;
uint8_t flags_value = static_cast<uint8_t>(fixed_header.packet_flags.to_ulong());

View File

@@ -8,30 +8,87 @@
#include <variable_byte_int.hpp>
#include <vector>
class PacketInterface {
/**
* @brief Interface class for network control packets.
*
* This class serves as an interface for network control packets in MQTT communication.
*/
class IPacket {
public:
PacketInterface(const std::vector<std::byte> &);
PacketInterface(const PacketInterface &);
PacketInterface();
virtual ~PacketInterface() = default;
/**
* @brief Constructs a new IPacket object from a byte vector.
*
* @param data The byte vector containing the packet data.
*/
IPacket(const std::vector<std::byte> &data);
/**
* @brief Copy constructor.
*
* @param other The IPacket object to copy.
*/
IPacket(const IPacket &other);
/**
* @brief Default constructor.
*/
IPacket();
/**
* @brief Virtual destructor.
*/
virtual ~IPacket() = default;
/**
* @brief Gets the length of the packet.
*
* @return The length of the packet in bytes.
*/
inline uint length() const { return sizeof(this->fixed_header) + this->fixed_header.remaining_length; }
/**
* @brief Gets the packet type.
*
* @return The packet type.
*/
inline PacketType get_packet_type() const { return this->fixed_header.packet_type; };
/**
* @brief Gets the packet flags.
*
* @return The packet flags.
*/
inline std::bitset<4> get_packet_flags() const { return this->fixed_header.packet_flags; };
/**
* @brief Converts the packet to a string representation.
*
* @return The string representation of the packet.
*/
virtual std::string as_string() const;
/**
* @brief Converts the packet to a byte vector.
*
* @return The byte vector representation of the packet.
*/
virtual std::vector<std::byte> as_bytes();
protected:
std::vector<std::byte> raw_data; /**< The raw data of the packet. */
FixedHeader fixed_header; /**< The fixed header of the packet. */
std::vector<std::byte> raw_data;
FixedHeader fixed_header;
/**
* @brief Parses the variable header of the packet.
*/
virtual void parse_variable_header() { throw NotImplemented(); };
/**
* @brief Parses the payload of the packet.
*/
virtual void parse_payload() { throw NotImplemented(); };
std::vector<std::byte>::const_iterator variable_header_start_byte;
std::vector<std::byte>::const_iterator variable_header_start_byte; /**< Iterator pointing to the start of the variable header. */
};
#endif // INCLUDE_NETWORK_CONTROL_PACKET_HPP_

View File

@@ -14,63 +14,106 @@
#include <variable_byte_int.hpp>
#include <variant>
/**
* @brief Enumeration of MQTT Control Packet Property Identifiers.
*/
enum class PropertyIdentifier : uint32_t {
PAYLOAD_FORMAT_INDICATOR = 1,
MESSAGE_EXPIRY_INTERVAL = 2,
CONTENT_TYPE = 3,
RESPONSE_TOPIC = 8,
CORRELATION_DATA = 9,
SUBSCRIPTION_IDENTIFIER = 11,
SESSION_EXPIRY_INTERVAL = 17,
ASSIGNED_CLIENT_IDENTIFIER = 18,
SERVER_KEEP_ALIVE = 19,
AUTHENTICATION_METHOD = 21,
AUTHENTICATION_DATA = 22,
REQUEST_PROBLEM_INFORMATION = 23,
WILL_DELAY_INTERVAL = 24,
REQUEST_RESPONSE_INFORMATION = 25,
RESPONSE_INFORMATION = 26,
SERVER_REFERENCE = 28,
REASON_STRING = 31,
RECEIVE_MAXIMUM = 33,
TOPIC_ALIAS_MAXIMUM = 34,
TOPIC_ALIAS = 35,
MAXIMUM_QOS = 36,
RETAIN_AVAILABLE = 37,
USER_PROPERTY = 38,
MAXIMUM_PACKET_SIZE = 39,
WILDCARD_SUBSCRIPTION_AVAILABLE = 40,
SUBSCRIPTION_IDENTIFIER_AVAILABLE = 41,
SHARED_SUBSCRIPTION_AVAILABLE = 42
PAYLOAD_FORMAT_INDICATOR = 1, /**< Payload Format Indicator. */
MESSAGE_EXPIRY_INTERVAL = 2, /**< Message Expiry Interval. */
CONTENT_TYPE = 3, /**< Content Type. */
RESPONSE_TOPIC = 8, /**< Response Topic. */
CORRELATION_DATA = 9, /**< Correlation Data. */
SUBSCRIPTION_IDENTIFIER = 11, /**< Subscription Identifier. */
SESSION_EXPIRY_INTERVAL = 17, /**< Session Expiry Interval. */
ASSIGNED_CLIENT_IDENTIFIER = 18, /**< Assigned Client Identifier. */
SERVER_KEEP_ALIVE = 19, /**< Server Keep Alive. */
AUTHENTICATION_METHOD = 21, /**< Authentication Method. */
AUTHENTICATION_DATA = 22, /**< Authentication Data. */
REQUEST_PROBLEM_INFORMATION = 23, /**< Request Problem Information. */
WILL_DELAY_INTERVAL = 24, /**< Will Delay Interval. */
REQUEST_RESPONSE_INFORMATION = 25, /**< Request Response Information. */
RESPONSE_INFORMATION = 26, /**< Response Information. */
SERVER_REFERENCE = 28, /**< Server Reference. */
REASON_STRING = 31, /**< Reason String. */
RECEIVE_MAXIMUM = 33, /**< Receive Maximum. */
TOPIC_ALIAS_MAXIMUM = 34, /**< Topic Alias Maximum. */
TOPIC_ALIAS = 35, /**< Topic Alias. */
MAXIMUM_QOS = 36, /**< Maximum QoS. */
RETAIN_AVAILABLE = 37, /**< Retain Available. */
USER_PROPERTY = 38, /**< User Property. */
MAXIMUM_PACKET_SIZE = 39, /**< Maximum Packet Size. */
WILDCARD_SUBSCRIPTION_AVAILABLE = 40, /**< Wildcard Subscription Available. */
SUBSCRIPTION_IDENTIFIER_AVAILABLE = 41, /**< Subscription Identifier Available. */
SHARED_SUBSCRIPTION_AVAILABLE = 42 /**< Shared Subscription Available. */
};
using UserProperty = std::queue<UTF8StringPair>;
using PropertyValue = std::variant<std::byte, uint32_t, uint16_t, int_vb, binary_data, utf8_str, UserProperty>;
using PropertyMap = std::map<PropertyIdentifier, PropertyValue>;
using UserProperty = std::queue<UTF8StringPair>; /**< User Property type definition. */
using PropertyValue = std::variant<std::byte, uint32_t, uint16_t, int_vb, binary_data, utf8_str, UserProperty>; /**< Property Value type definition. */
using PropertyMap = std::map<PropertyIdentifier, PropertyValue>; /**< Property Map type definition. */
std::ostream &operator<<(std::ostream &os, const UserProperty &value);
std::ostream &operator<<(std::ostream &os, const PropertyIdentifier &value);
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);
std::ostream &operator<<(std::ostream &os, const PropertyMap &value);
/**
* @brief Class representing MQTT Control Packet Properties.
*/
class MQTTProperties {
public:
/**
* @brief Default constructor.
*/
MQTTProperties();
/**
* @brief Constructs MQTTProperties from a byte vector.
*
* @param property_start Iterator pointing to the start of the property data in the byte vector.
*/
MQTTProperties(const std::vector<std::byte>::const_iterator &property_start);
/**
* @brief Destructor.
*/
~MQTTProperties() = default;
/**
* @brief Converts MQTTProperties to a byte vector.
*
* @return Byte vector representation of MQTTProperties.
*/
std::vector<std::byte> as_bytes() const;
/**
* @brief Gets the size of MQTTProperties.
*
* @return The size of MQTTProperties.
*/
uint16_t size() const { return this->length; };
/**
* @brief Gets the property value for the specified property identifier.
*
* @param prop The property identifier.
* @return The property value.
*/
PropertyValue get_property(const PropertyIdentifier &prop) { return this->properties[prop]; };
/**
* @brief Overloads the subscript operator to access properties by identifier.
*
* @param prop The property identifier.
* @return The property value.
*/
PropertyValue &operator[](PropertyIdentifier prop);
friend std::ostream &operator<<(std::ostream &os, const std::byte &value);
friend std::ostream &operator<<(std::ostream &os, const MQTTProperties &value);
PropertyValue &operator[](PropertyIdentifier);
private:
int_vb length;
PropertyMap properties;
int_vb length; /**< Length of the properties. */
PropertyMap properties; /**< Map containing MQTT properties. */
};
#endif // INCLUDE_CONTROL_PACKET_PROPERTY_HPP_

View File

@@ -44,7 +44,6 @@ void Session::close() {
this->is_session_alive = false;
if (this->keepalive_thread.joinable())
this->keepalive_thread.join();
}
std::size_t Session::send(const std::vector<std::byte> &buffer) {
@@ -70,8 +69,7 @@ void Session::close_if_not_connected(uint timeout_sec) {
return;
std::ostringstream log_msg;
log_msg << "Session timed out while waiting for CONENCT packet"
<< " (timeout is " << timeout_sec << " seconds)";
log_msg << "Session timed out while waiting for CONNECT packet (timeout is " << timeout_sec << " seconds)";
spdlog::warn(log_msg.str());
this->close(DisconnectReasonCode::MAXIMUM_CONNECT_TIMEOUT);
}
@@ -104,7 +102,7 @@ void Session::listen() {
return;
}
this->current_packet = new PacketInterface(buffer);
this->current_packet = new IPacket(buffer);
switch (this->current_packet->get_packet_type()) {
case PacketType::CONNECT: {
this->set_state(StateConnect::get_instance());

View File

@@ -16,49 +16,120 @@
class ISessionState;
#include <state_interface.hpp>
const unsigned int buffer_size = 2048;
/**
* @brief Class representing a session in the MQTT protocol.
*
* This class manages the communication session with an MQTT client.
*/
class Session {
public:
/**
* @brief Default constructor.
*/
Session();
/**
* @brief Constructor initializing the session with a socket file descriptor.
*
* @param socket_fd The socket file descriptor.
*/
Session(int socket_fd);
/**
* @brief Destructor.
*/
~Session();
std::string client_id;
std::string client_id; /**< The client ID associated with the session. */
/**
* @brief Starts listening for incoming data on the session socket.
*/
void listen();
/**
* @brief Closes the session.
*/
void close();
void close(const DisconnectReasonCode &);
void set_state(ISessionState &);
/**
* @brief Closes the session with a specific reason code.
*
* @param reason_code The reason code for disconnection.
*/
void close(const DisconnectReasonCode &reason_code);
/**
* @brief Sets the current state of the session.
*
* @param state The state to set.
*/
void set_state(ISessionState &state);
/**
* @brief Checks if the session is alive.
*
* @return True if the session is alive, false otherwise.
*/
inline bool is_alive() const { return this->is_session_alive; };
/**
* @brief Checks if the session is connected.
*
* @return True if the session is connected, false otherwise.
*/
inline bool is_connected() const { return this->is_session_connected; };
inline PacketInterface *get_current_packet() const { return this->current_packet; };
/**
* @brief Gets the current packet being processed by the session.
*
* @return A pointer to the current packet.
*/
inline IPacket *get_current_packet() const { return this->current_packet; };
/**
* @brief Gets the current state of the session.
*
* @return A pointer to the current state.
*/
inline ISessionState *get_current_state() const { return this->current_state; };
std::size_t send(const std::vector<std::byte> &);
/**
* @brief Sends data over the session socket.
*
* @param buffer The data to send.
* @return The number of bytes sent.
*/
std::size_t send(const std::vector<std::byte> &buffer);
std::function<void(Session *)> on_connect;
std::function<void(Session *)> on_disconnect;
std::function<void(Session *)> on_connect; /**< Callback function invoked when a client connects. */
std::function<void(Session *)> on_disconnect; /**< Callback function invoked when a client disconnects. */
protected:
friend class StateConnect;
friend class StateDisconnect;
std::atomic<bool> is_session_alive;
std::atomic<bool> is_session_connected;
std::atomic<bool> is_session_alive; /**< Flag indicating if the session is alive. */
std::atomic<bool> is_session_connected; /**< Flag indicating if the session is connected. */
private:
ISessionState *current_state;
PacketInterface *current_packet;
int socket;
std::thread keepalive_thread;
ISessionState *current_state; /**< Pointer to the current session state. */
IPacket *current_packet; /**< Pointer to the current packet being processed. */
int socket; /**< Socket file descriptor for the session. */
std::thread keepalive_thread; /**< Thread for handling keepalive messages. */
const unsigned int buffer_size = 65535; /**< Max TCP packet bytes accepted in a single receive call. */
/**
* @brief Joins the keepalive thread.
*/
void join();
/**
* @brief Closes the session if it's not connected within a timeout period.
*
* @param timeout_sec Timeout period in seconds.
*/
void close_if_not_connected(uint timeout_sec = 10);
time_t last_keepalive;
};
#endif // INCLUDE_PROTOCOL_SESSION_HPP_

View File

@@ -35,7 +35,7 @@ void StateConnect::process(Session *session) {
ConnectACK ack_packet;
ack_packet.set_session_present(false);
try {
PacketInterface *packet_interface = session->get_current_packet();
IPacket *packet_interface = session->get_current_packet();
ConnectPacket packet(*packet_interface);
// TODO: Validate and auth packet

View File

@@ -3,17 +3,49 @@
#include "state_interface.hpp"
/**
* @brief Class representing the state of establishing a connection in the MQTT protocol.
*
* This class defines the behavior and transitions associated with the connection state of a session.
*/
class StateConnect : public ISessionState {
public:
/**
* @brief Method called when entering the connection state.
*
* @param session Pointer to the session associated with the state.
*/
void enter(Session *session) final;
/**
* @brief Method called to process the session in the connection state.
*
* @param session Pointer to the session associated with the state.
*/
void process(Session *session) final;
/**
* @brief Method called when exiting the connection state.
*
* @param session Pointer to the session associated with the state.
*/
void exit(Session *session) final;
/**
* @brief Get the singleton instance of the connection state.
*
* @return Reference to the singleton instance of the connection state.
*/
static ISessionState &get_instance();
private:
StateConnect() {};
StateConnect(const StateConnect &);
StateConnect &operator=(const StateConnect &);
/**
* @brief Private constructor to prevent external instantiation.
*/
StateConnect(){};
StateConnect(const StateConnect &); /**< Copy constructor. */
StateConnect &operator=(const StateConnect &); /**< Assignment operator. */
};
#endif // INCLUDE_FTM_STATE_CONNECT_HPP_

View File

@@ -22,7 +22,7 @@ void StateDisconnect::exit(Session *session) {
}
void StateDisconnect::process(Session *session) {
PacketInterface *packet_interface = session->get_current_packet();
IPacket *packet_interface = session->get_current_packet();
DisconnectPacket disconnect_packet(*packet_interface);
std::ostringstream log_msg;

View File

@@ -3,18 +3,49 @@
#include "state_interface.hpp"
/**
* @brief Class representing the state of disconnecting in the MQTT protocol.
*
* This class defines the behavior and transitions associated with the disconnect state of a session.
*/
class StateDisconnect : public ISessionState {
public:
/**
* @brief Method called when entering the disconnect state.
*
* @param session Pointer to the session associated with the state.
*/
void enter(Session *session) final;
/**
* @brief Method called to process the session in the disconnect state.
*
* @param session Pointer to the session associated with the state.
*/
void process(Session *session) final;
/**
* @brief Method called when exiting the disconnect state.
*
* @param session Pointer to the session associated with the state.
*/
void exit(Session *session) final;
/**
* @brief Get the singleton instance of the disconnect state.
*
* @return Reference to the singleton instance of the disconnect state.
*/
static ISessionState &get_instance();
private:
StateDisconnect() {};
StateDisconnect(const StateDisconnect &);
StateDisconnect &operator=(const StateDisconnect &);
/**
* @brief Private constructor to prevent external instantiation.
*/
StateDisconnect(){};
StateDisconnect(const StateDisconnect &); /**< Copy constructor. */
StateDisconnect &operator=(const StateDisconnect &); /**< Assignment operator. */
};
#endif // INCLUDE_STATES_STATE_DISCONNECT_HPP_
#endif // INCLUDE_STATES_STATE_DISCONNECT_HPP_

View File

@@ -5,17 +5,44 @@
// Forward declaration to resolve circular dependency
class Session;
#include <session.hpp>
/**
* @brief Interface class for defining session states in the MQTT protocol.
*
* This class serves as an interface for defining different states of a session in the MQTT protocol.
*/
class ISessionState {
public:
/**
* @brief Virtual destructor.
*/
virtual ~ISessionState(){};
virtual void enter(Session *) = 0;
virtual void process(Session *) = 0;
virtual void exit(Session *) = 0;
/**
* @brief Method called when entering the state.
*
* @param session Pointer to the session associated with the state.
*/
virtual void enter(Session * session) = 0;
/**
* @brief Method called to process the session in the state.
*
* @param session Pointer to the session associated with the state.
*/
virtual void process(Session * session) = 0;
/**
* @brief Method called when exiting the state.
*
* @param session Pointer to the session associated with the state.
*/
virtual void exit(Session * session) = 0;
private:
PacketInterface packet;
IPacket packet; /**< Packet associated with the state. */
};
#endif // INCLUDE_FTM_STATE_INTERFACE_HPP_

View File

@@ -3,19 +3,49 @@
#include "state_interface.hpp"
/**
* @brief Class representing the state of waiting for a connection in the MQTT protocol.
*
* This class defines the behavior and transitions associated with the state of waiting for a connection in a session.
*/
class StateWaitingConnection : public ISessionState {
public:
/**
* @brief Method called when entering the state of waiting for a connection.
*
* @param session Pointer to the session associated with the state.
*/
void enter(Session *session) final;
/**
* @brief Method called to process the session in the state of waiting for a connection.
*
* @param session Pointer to the session associated with the state.
*/
void process(Session *session) final;
/**
* @brief Method called when exiting the state of waiting for a connection.
*
* @param session Pointer to the session associated with the state.
*/
void exit(Session *session) final;
/**
* @brief Get the singleton instance of the state of waiting for a connection.
*
* @return Reference to the singleton instance of the state of waiting for a connection.
*/
static ISessionState &get_instance();
private:
/**
* @brief Private constructor to prevent external instantiation.
*/
StateWaitingConnection() {};
StateWaitingConnection(const StateWaitingConnection &);
StateWaitingConnection &operator=(const StateWaitingConnection &);
StateWaitingConnection(const StateWaitingConnection &); /**< Copy constructor. */
StateWaitingConnection &operator=(const StateWaitingConnection &); /**< Assignment operator. */
};
#endif // INCLUDE_STATES_STATE_WAITING_CONNECTION_HPP_

View File

@@ -1,29 +1,67 @@
#ifndef INCLUDE_TYPES_BINARY_DATA_HPP_
#define INCLUDE_TYPES_BINARY_DATA_HPP_
#include <type_interface.hpp>
#include <cstdint>
#include <ostream>
#include <type_interface.hpp>
#include <vector>
/**
* @brief Class representing binary data.
*
* This class provides functionality to work with binary data, including conversion to string and byte vector.
*/
class BinaryData : public TypeInterface {
public:
// Create a BinaryData from the start of byte vector
/**
* @brief Default constructor.
*
* Constructs an empty BinaryData object.
*/
BinaryData();
/**
* @brief Constructor to create BinaryData from a byte vector iterator.
*
* Constructs a BinaryData object from the provided byte vector iterator.
*
* @param data_begin Iterator pointing to the beginning of the byte vector.
*/
BinaryData(const std::vector<std::byte>::const_iterator &data_begin);
/**
* @brief Destructor.
*/
~BinaryData() = default;
friend std::ostream &operator<<(std::ostream &, const BinaryData&);
/**
* @brief Get the size of the binary data.
*
* @return The size of the binary data in bytes.
*/
uint16_t size() const final { return data_size; };
/**
* @brief Convert the binary data to a string representation.
*
* @return A string representing the binary data in hexadecimal.
*/
std::string as_string() const final;
/**
* @brief Convert the binary data to a byte vector.
*
* @return A byte vector representing the binary data.
*/
std::vector<std::byte> as_bytes() const final;
friend std::ostream &operator<<(std::ostream &, const BinaryData &);
protected:
std::vector<std::byte> data;
uint16_t data_size;
std::vector<std::byte> data; /**< The binary data. */
uint16_t data_size; /**< The size of the binary data in bytes. */
};
using binary_data = BinaryData;
#endif // INCLUDE_TYPES_BINARY_DATA_HPP_
#endif // INCLUDE_TYPES_BINARY_DATA_HPP_

View File

@@ -5,12 +5,37 @@
#include <string>
#include <vector>
/**
* @brief Interface class for types.
*
* This class serves as an interface for various types, providing common functionality such as size retrieval and conversion to string and byte vector.
*/
class TypeInterface {
public:
/**
* @brief Virtual destructor.
*/
virtual ~TypeInterface() = default;
/**
* @brief Get the size of the type.
*
* @return The size of the type in bytes.
*/
virtual uint16_t size() const = 0;
/**
* @brief Convert the type to a string representation.
*
* @return A string representing the type.
*/
virtual std::string as_string() const = 0;
/**
* @brief Convert the type to a byte vector.
*
* @return A byte vector representing the type.
*/
virtual std::vector<std::byte> as_bytes() const = 0;
};

View File

@@ -1,27 +1,70 @@
#ifndef INCLUDE_TYPES_UTF8_STRING_HPP_
#define INCLUDE_TYPES_UTF8_STRING_HPP_
#include <cstddef>
#include <type_interface.hpp>
#include <binary_data.hpp>
#include <cstddef>
#include <string>
#include <type_interface.hpp>
#include <vector>
/**
* @brief Class representing a UTF-8 string.
*
* This class provides functionality to work with UTF-8 encoded strings, including conversion to byte vector and string representation.
*/
class UTF8String : public TypeInterface {
public:
/**
* @brief Default constructor.
*/
UTF8String();
/**
* @brief Constructor that initializes the UTF-8 string from a byte vector.
*
* @param data_begin Iterator pointing to the start of the byte vector containing the UTF-8 string data.
*/
UTF8String(const std::vector<std::byte>::const_iterator &data_begin);
/**
* @brief Virtual destructor.
*/
~UTF8String() = default;
/**
* @brief Get the size of the UTF-8 string.
*
* @return The char count of the UTF-8 string.
*/
uint16_t size() const final { return static_cast<uint16_t>(utf8_string.size()); };
/**
* @brief Convert the UTF-8 string to a string representation.
*
* @return A string representing the UTF-8 string.
*/
std::string as_string() const final { return this->utf8_string; };
/**
* @brief Convert the UTF-8 string to a byte vector.
*
* @return A byte vector representing the UTF-8 string.
*/
std::vector<std::byte> as_bytes() const final;
/**
* @brief Overloaded output stream operator for UTF8String.
*
* @param os The output stream.
* @param value The UTF8String object.
* @return The output stream.
*/
friend std::ostream &operator<<(std::ostream &os, const UTF8String &value);
protected:
std::string utf8_string;
std::string utf8_string; /**< The UTF-8 string. */
};
using utf8_str = UTF8String;
#endif // INCLUDE_TYPES_UTF8_STRING_HPP_

View File

@@ -4,23 +4,70 @@
#include <type_interface.hpp>
#include <utf8_string.hpp>
/**
* @brief Class representing a pair of UTF-8 strings.
*
* This class provides functionality to work with pairs of UTF-8 strings, including conversion to byte vector and string representation.
*/
class UTF8StringPair : public TypeInterface {
public:
/**
* @brief Default constructor.
*/
UTF8StringPair();
/**
* @brief Constructor that initializes the UTF-8 string pair from a byte vector.
*
* @param data_begin Iterator pointing to the start of the byte vector containing the UTF-8 string pair data.
*/
UTF8StringPair(const std::vector<std::byte>::const_iterator &data_begin);
/**
* @brief Virtual destructor.
*/
~UTF8StringPair() = default;
friend std::ostream &operator<<(std::ostream &os, const UTF8StringPair &value);
/**
* @brief Get the size of the UTF-8 string pair.
*
* @return The total character count of the UTF-8 string pair.
*/
uint16_t size() const final { return key.size() + value.size(); };
/**
* @brief Convert the UTF-8 string pair to a string representation.
*
* @return A string representing the UTF-8 string pair.
*/
std::string as_string() const final { return key.as_string() + "=" + value.as_string(); };
/**
* @brief Convert the UTF-8 string pair to a byte vector.
*
* @return A byte vector representing the UTF-8 string pair.
*/
std::vector<std::byte> as_bytes() const final;
/**
* @brief Get the key of the UTF-8 string pair.
*
* @return The key of the UTF-8 string pair.
*/
inline const UTF8String get_key() const { return this->key; };
/**
* @brief Get the value of the UTF-8 string pair.
*
* @return The value of the UTF-8 string pair.
*/
inline const UTF8String get_value() const { return this->value; };
private:
utf8_str key, value;
UTF8String key; /**< The key of the UTF-8 string pair. */
UTF8String value; /**< The value of the UTF-8 string pair. */
};
#endif // INCLUDE_TYPES_UTF8_STRING_PAIR_HPP_

View File

@@ -1,19 +1,20 @@
#ifndef INCLUDE_TYPES_VARIABLE_BYTE_INT_HPP_
#define INCLUDE_TYPES_VARIABLE_BYTE_INT_HPP_
#include <bit.hpp>
#include <cstdint>
#include <ostream>
#include <type_interface.hpp>
#include <type_traits>
#include <bit.hpp>
#include <vector>
/**
* @brief Class representing a Variable Byte Integer according to MQTT v5 section 1.5.5.
*
*
* This class provides functionality to handle Variable Byte Integers as defined in the MQTT v5 specification.
*
* For more details, refer to: <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011">MQTT v5.0 specification, section 1.5.5</a>.
*
* For more details
* @see file:///files/breno/mqttd/docs/mqtt_spec/MQTT%20Version%205.0.html#_Toc3901011
*/
class VariableByteInteger : public TypeInterface {
public:
@@ -24,14 +25,14 @@ public:
/**
* @brief Creates a new Variable Byte Integer object from an integral integer value.
*
*
* @param decoded_value The integral value to initialize the Variable Byte Integer with.
*/
VariableByteInteger(const uint32_t &decoded_value);
/**
* @brief Creates a new Variable Byte Integer object from a byte vector representing an encoded Variable Byte Integer.
*
*
* @param encoded_value The byte vector representing the encoded Variable Byte Integer.
* The vector must have at least 4 bytes, further bytes will be ignored.
*/
@@ -39,7 +40,7 @@ public:
/**
* @brief Creates a new Variable Byte Integer object from a byte vector iterator pointing to the beginning of the data representing an encoded Variable Byte Integer.
*
*
* @param encoded_value_start The iterator pointing to the beginning of the data representing the encoded Variable Byte Integer.
* The vector must have at least 4 bytes, further bytes will be ignored.
*/
@@ -49,28 +50,28 @@ public:
/**
* @brief Gets the size of the Variable Byte Integer in bytes.
*
*
* @return The size of the Variable Byte Integer in bytes.
*/
uint16_t size() const final { return this->bytes_count; };
/**
* @brief Gets the decoded value of the Variable Byte Integer as a string.
*
*
* @return The decoded value of the Variable Byte Integer as a string.
*/
std::string as_string() const final { return std::to_string(this->decoded_value); };
/**
* @brief Gets the byte array representing the encoded value of a Variable Byte Integer.
*
*
* @return The byte array representing the encoded value of a Variable Byte Integer.
*/
std::vector<std::byte> as_bytes() const final { return this->encoded_value; };
/**
* @brief Overloads the assignment operator for integral types.
*
*
* Enables assignment of a Variable Byte Integer from an integer.
*/
template <typename T> typename std::enable_if<std::is_integral<T>::value, VariableByteInteger &>::type operator=(T &);
@@ -79,28 +80,28 @@ public:
* @brief Overloads the output stream operator to print a Variable Byte Integer as its decoded value.
*/
friend std::ostream &operator<<(std::ostream &, const VariableByteInteger &);
/**
* @brief Conversion operator to uint32_t.
*
*
* @return The value of the Variable Byte Integer as a uint32_t.
*/
inline operator uint32_t() const { return this->decoded_value; };
/**
* @brief Gets the maximum value allowed for a Variable Byte Integer.
*
*
* @return The maximum value allowed for a Variable Byte Integer.
*/
inline uint32_t max() const { return 268435455; };
private:
std::vector<std::byte> encoded_value; /**< The value of the Variable Byte integer. */
std::vector<std::byte> encoded_value; /**< The value of the Variable Byte integer. */
std::vector<std::byte> encode(const unsigned int &); /**< Encodes an integral type to a byte vector representing a Variable Byte Integer. */
uint32_t decoded_value; /**< The value of the Variable Byte Integer as an integer. */
uint32_t decoded_value; /**< The value of the Variable Byte Integer as an integer. */
uint32_t decode(const std::vector<std::byte> &); /**< Decodes a byte vector to an integer. */
uint8_t bytes_count; /**< How many bytes the #encoded_value has. */
uint8_t bytes_count; /**< How many bytes the #encoded_value has. */
};
using int_vb = VariableByteInteger;

View File

@@ -3,15 +3,35 @@
namespace utilities {
namespace bit {
template <typename T> T set(T num, int position) {
/**
* @brief Set a bit at a specified position in a value.
*
* @tparam T The type of the value.
* @param value The value to set the bit in.
* @param position The position of the bit to set.
* @return The value with the specified bit set.
*/
template <typename T>
T set(T value, int position) {
T mask = static_cast<T>(1) << position;
return num | mask;
return value | mask;
}
template <typename T> bool get(T num, int position) {
/**
* @brief Get the value of a bit at a specified position.
*
* @tparam T The type of the value.
* @param value The value to get the bit from.
* @param position The position of the bit to get.
* @return True if the bit at the specified position is set, false otherwise.
*/
template <typename T>
bool get(T value, int position) {
T mask = static_cast<T>(1) << position;
return (num & mask) != 0;
return (value & mask) != 0;
}
} // namespace bit
} // namespace utilities

View File

@@ -1,13 +1,16 @@
#include <bytes.hpp>
#include <algorithm>
#include <bytes.hpp>
namespace utilities {
namespace bytes {
std::vector<std::byte> to_bytes(const std::string &value) {
std::vector<std::byte> bytes;
bytes.reserve(value.size());
std::transform(std::begin(value), std::end(value), std::back_inserter(bytes), [](char c) { return std::byte(c); });
return bytes;
}
} // namespace bytes
} // namespace utilities

View File

@@ -7,12 +7,27 @@
namespace utilities {
namespace bytes {
/**
* @brief Convert a string to a vector of bytes.
*
* @param value The string to convert.
* @return A vector containing the bytes of the string.
*/
std::vector<std::byte> to_bytes(const std::string &value);
template <typename T> std::vector<std::byte> to_bytes(const T &value) {
/**
* @brief Convert a value to a vector of bytes.
*
* @tparam T The type of the value.
* @param value The value to convert.
* @return A vector containing the bytes of the value.
*/
template <typename T>
std::vector<std::byte> to_bytes(const T &value) {
const std::byte *byte_ptr = reinterpret_cast<const std::byte *>(&value);
return std::vector<std::byte>(byte_ptr, byte_ptr + sizeof(T));
}
} // namespace bytes
} // namespace utilities

View File

@@ -7,7 +7,16 @@
namespace utilities {
namespace types {
template <typename T> typename std::enable_if<std::is_integral<T>::value, T>::type get_fixed_size_integer(const std::vector<std::byte>::const_iterator &it) {
/**
* @brief Extracts a fixed-size integer value from a byte vector.
*
* @tparam T The type of the integer.
* @param it Iterator pointing to the beginning of the integer bytes in the vector.
* @return The extracted integer value.
*/
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type get_fixed_size_integer(const std::vector<std::byte>::const_iterator &it) {
T value = 0;
for (unsigned int i = 0; i < sizeof(T); i++) {
uint8_t byte_value = std::to_integer<uint8_t>(*(std::next(it, i)));
@@ -15,6 +24,7 @@ template <typename T> typename std::enable_if<std::is_integral<T>::value, T>::ty
}
return value;
}
} // namespace types
} // namespace utilities

View File

@@ -1,29 +1,67 @@
#include "version.hpp"
#include <connection_listener.hpp>
#include <cpptrace/cpptrace.hpp>
#include <csignal>
#include <iostream>
#include "version.hpp"
#include <backtrace.hpp>
#include <connection_listener.hpp>
#include <set>
#include <sys/wait.h>
ConnectionListener clistener;
struct pipe_t {
union {
struct {
int read_end;
int write_end;
};
int data[2];
};
};
static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing");
void send_trace(cpptrace::frame_ptr *buffer, std::size_t size) {
pipe_t input_pipe;
pipe(input_pipe.data);
const pid_t pid = fork();
if (pid == -1)
return;
if (pid == 0) { // child
dup2(input_pipe.read_end, STDIN_FILENO);
close(input_pipe.read_end);
close(input_pipe.write_end);
execl("mqttd_stacktracer", "mqttd_stacktracer", nullptr);
_exit(1);
}
for (std::size_t i = 0; i < size; i++) {
cpptrace::safe_object_frame frame;
cpptrace::get_safe_object_frame(buffer[i], &frame);
write(input_pipe.write_end, &frame, sizeof(frame));
}
close(input_pipe.read_end);
close(input_pipe.write_end);
waitpid(pid, nullptr, 0);
}
void termination_handler(int signal_num) {
spdlog::info("Received signal " + std::string(strsignal(signal_num)));
std::set<unsigned int> backtrace_signals = {SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGABRT};
std::set<unsigned int> backtrace_signals = {SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGABRT, SIGTRAP};
auto is_backtrace_signal = backtrace_signals.find(signal_num);
if (is_backtrace_signal != backtrace_signals.end()) {
backtrace::backtrace();
exit(ExitCode::ERROR);
constexpr std::size_t SIZE = 256;
cpptrace::frame_ptr buffer[SIZE];
std::size_t count = cpptrace::safe_generate_raw_trace(buffer, SIZE);
send_trace(buffer, count);
_exit(ExitCode::ERROR);
}
clistener.stop();
exit(ExitCode::SUCCESS);
}
void set_signal_handlers() {
std::set<unsigned int> termination_signals = {SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSYS, SIGABRT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
std::set<unsigned int> termination_signals = {SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSYS, SIGABRT, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTRAP};
// Blocking signals with set
sigset_t block_mask;
@@ -43,9 +81,21 @@ void set_signal_handlers() {
}
}
void warmup_cpptrace() {
cpptrace::frame_ptr buffer[10];
cpptrace::safe_generate_raw_trace(buffer, 10);
cpptrace::safe_object_frame frame;
cpptrace::get_safe_object_frame(buffer[0], &frame);
}
int main() {
std::cout << "MQTTd Version " << MQTTD_VERSION_MAJOR << "." << MQTTD_VERSION_MINOR << "." << MQTTD_VERSION_PATCH << std::endl;
cpptrace::absorb_trace_exceptions(false);
cpptrace::register_terminate_handler();
cpptrace::enable_inlined_call_resolution(true);
warmup_cpptrace();
set_signal_handlers();
raise(SIGSEGV);
// spdlog::set_level(spdlog::level::debug);

22
src/mqttd_stacktracer.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include <cpptrace/cpptrace.hpp>
#include <cstdio>
#include <iostream>
#include <unistd.h>
int main() {
cpptrace::object_trace trace;
while (true) {
cpptrace::safe_object_frame frame;
std::size_t res = fread(&frame, sizeof(frame), 1, stdin);
if (res == 0)
break;
else if (res == 1){
trace.frames.push_back(frame.resolve());
}
else {
std::cerr << "Oops, size mismatch " << res << " " << sizeof(frame) << std::endl;
break;
}
}
trace.resolve().print();
}

View File

@@ -1,5 +1,5 @@
#define MQTTD_VERSION_MAJOR 0
#define MQTTD_VERSION_MINOR 0
#define MQTTD_VERSION_PATCH 1
#define MQTTD_COMMIT_HASH f91fe172be4c51f393bdd4184e62281d75eac64e
#define MQTTD_BUILD_TIMESTAMP 1707947036
#define MQTTD_COMMIT_HASH ccd31f1802fa0052819a18599c9932e1c74e8a1a
#define MQTTD_BUILD_TIMESTAMP 1713729654