basic documentation using ChatGPT and proper backtrace
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
14
cmake/GetCppTrace.cmake
Normal 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)
|
||||
@@ -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")
|
||||
|
||||
27425
docs/mqtt_spec/MQTT Version 5.0.html
Normal file
27425
docs/mqtt_spec/MQTT Version 5.0.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/mqtt_spec/MQTT Version 5.0_files/OASISLogo-v2.0.jpg
Normal file
BIN
docs/mqtt_spec/MQTT Version 5.0_files/OASISLogo-v2.0.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
19735
docs/mqtt_spec/MQTT Version 5.0_files/page-script.js
Normal file
19735
docs/mqtt_spec/MQTT Version 5.0_files/page-script.js
Normal file
File diff suppressed because it is too large
Load Diff
4
scripts/crashlog.txt
Normal file
4
scripts/crashlog.txt
Normal 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
BIN
scripts/mqttd
Executable file
Binary file not shown.
53
scripts/process_crashlog.py
Executable file
53
scripts/process_crashlog.py
Executable 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
7
setup.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
[isort]
|
||||
profile = black
|
||||
|
||||
[pycodestyle]
|
||||
max-line-length = 128
|
||||
ignore = E203,E701, E402, E501, E722
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
add_library(libcore "")
|
||||
|
||||
add_subdirectory(backtrace)
|
||||
add_subdirectory(broker)
|
||||
|
||||
FILE(GLOB CPP_FILES CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
22
src/mqttd_stacktracer.cpp
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user