From 44ebf08f1f1c419f238408c7a6f711e15098ec08 Mon Sep 17 00:00:00 2001 From: Francois Michaut Date: Sat, 20 Jun 2026 09:45:59 -0400 Subject: [PATCH] Configurable Server Endpoint - Server endpoint now lives in the server config - Required serialization of Endpoint class - Added an error for missing config file --- CMakeLists.txt | 3 +- include/FileShare/Config/Config.hpp | 4 +- include/FileShare/Config/Serialization.hpp | 55 +++++++++++++++++++++- include/FileShare/Config/ServerConfig.hpp | 15 +++++- include/FileShare/Error.hpp | 2 +- include/FileShare/Errors/ConfigErrors.hpp | 27 +++++++++++ include/FileShare/Peer/Peer.hpp | 5 +- include/FileShare/Server.hpp | 32 +++++++------ source/Config/Config.cpp | 13 +++-- source/Config/ServerConfig.cpp | 25 ++++++++-- source/Errors/ConfigErrors.cpp | 22 +++++++++ source/Peer/Peer.cpp | 6 +-- source/Server.cpp | 9 +--- 13 files changed, 175 insertions(+), 43 deletions(-) create mode 100644 include/FileShare/Errors/ConfigErrors.hpp create mode 100644 source/Errors/ConfigErrors.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e89693..adf6c4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ ## Author Francois Michaut ## ## Started on Thu May 26 23:23:59 2022 Francois Michaut -## Last update Mon Aug 18 17:26:23 2025 Francois Michaut +## Last update Thu May 14 04:43:48 2026 Francois Michaut ## ## CMakeLists.txt : CMake to build the FileShareProtocol library ## @@ -35,6 +35,7 @@ add_library(fsp source/Config/ServerConfig.cpp source/Errors/TransferErrors.cpp + source/Errors/ConfigErrors.cpp source/Peer/Peer.cpp source/Peer/PeerBase.cpp diff --git a/include/FileShare/Config/Config.hpp b/include/FileShare/Config/Config.hpp index 594c02c..4e32fa2 100644 --- a/include/FileShare/Config/Config.hpp +++ b/include/FileShare/Config/Config.hpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Tue Sep 13 11:23:57 2022 Francois Michaut -** Last update Fri Aug 22 18:59:52 2025 Francois Michaut +** Last update Thu May 14 05:03:33 2026 Francois Michaut ** ** Config.hpp : Configuration of the file sharing */ @@ -35,7 +35,7 @@ namespace FileShare { auto set_downloads_folder(const std::filesystem::path &path) -> Config &; auto set_file_mapping(FileMapping mapping) -> Config & { m_filemap = std::move(mapping); return *this; } - auto get_file_mapping() const -> const FileMapping & { return m_filemap; } + [[nodiscard]] auto get_file_mapping() const -> const FileMapping & { return m_filemap; } auto get_file_mapping() -> FileMapping & { return m_filemap; } [[nodiscard]] auto get_transport_mode() const -> TransportMode { return m_transport_mode; } diff --git a/include/FileShare/Config/Serialization.hpp b/include/FileShare/Config/Serialization.hpp index 71dfd24..a97ce15 100644 --- a/include/FileShare/Config/Serialization.hpp +++ b/include/FileShare/Config/Serialization.hpp @@ -4,32 +4,40 @@ ** Author Francois Michaut ** ** Started on Sun Dec 10 10:56:44 2023 Francois Michaut -** Last update Sat Aug 23 20:42:50 2025 Francois Michaut +** Last update Fri Jun 12 03:23:08 2026 Francois Michaut ** ** Serialization.hpp : FileShare Config serialization functions */ #pragma once +#include "CppSockets/Address.hpp" +#include "CppSockets/IPv4.hpp" #include "FileShare/Config/Config.hpp" #include "FileShare/Config/ServerConfig.hpp" +#include #include #include +#include #include #include #include static constexpr std::uint32_t FILE_SHARE_CONFIG_VERSION = 0; -static constexpr std::uint32_t FILE_SHARE_SERVER_CONFIG_VERSION = 0; +static constexpr std::uint32_t FILE_SHARE_SERVER_CONFIG_VERSION = 1; static constexpr std::uint32_t FILE_SHARE_FILE_MAPPING_VERSION = 0; static constexpr std::uint32_t FILE_SHARE_PATH_NODE_VERSION = 0; +static constexpr std::uint32_t CPP_SOCKETS_ENDPOINT_VERSION = 0; CEREAL_CLASS_VERSION(FileShare::Config, FILE_SHARE_CONFIG_VERSION); // NOLINT CEREAL_CLASS_VERSION(FileShare::ServerConfig, FILE_SHARE_SERVER_CONFIG_VERSION); // NOLINT CEREAL_CLASS_VERSION(FileShare::FileMapping, FILE_SHARE_FILE_MAPPING_VERSION); // NOLINT CEREAL_CLASS_VERSION(FileShare::RootPathNode, FILE_SHARE_PATH_NODE_VERSION); // NOLINT CEREAL_CLASS_VERSION(FileShare::PathNode, FILE_SHARE_PATH_NODE_VERSION); // NOLINT +CEREAL_CLASS_VERSION(CppSockets::EndpointV4, CPP_SOCKETS_ENDPOINT_VERSION); // NOLINT + +CEREAL_REGISTER_POLYMORPHIC_RELATION(CppSockets::IEndpoint, CppSockets::EndpointV4); // TODO: Make a custom cereal archive for .conf files : https://uscilab.github.io/cereal/serialization_archives.html#adding-more-archives // Use .conf files for Config and ServerConfig @@ -43,6 +51,12 @@ namespace FileShare { } if (version == FILE_SHARE_SERVER_CONFIG_VERSION) { + archive( + config.m_uuid, config.m_device_name, config.m_private_keys_dir, + config.m_private_key_name, config.m_disable_server, config.m_server_endpoint + ); + } else if (version == 0) { + // Defaults Server Endpoint archive( config.m_uuid, config.m_device_name, config.m_private_keys_dir, config.m_private_key_name, config.m_disable_server @@ -92,3 +106,40 @@ namespace FileShare { } } } + +namespace cereal { + template + void serialize(Archive &archive, CppSockets::EndpointV4 &endpoint, const std::uint32_t version) { + if (version > CPP_SOCKETS_ENDPOINT_VERSION) { + throw std::runtime_error("Endpoint file format is more recent than what this program supports"); + } + + if (version == CPP_SOCKETS_ENDPOINT_VERSION) { + archive(endpoint.get_addr().to_string(), endpoint.get_port()); + } else { + throw std::runtime_error("FileMapping file format is unsupported"); + } + } + + template <> struct LoadAndConstruct { + template + static void load_and_construct(Archive &archive, cereal::construct &construct, std::uint32_t const version) { + if (version > CPP_SOCKETS_ENDPOINT_VERSION) { + throw std::runtime_error("Endpoint file format is more recent than what this program supports"); + } + + if (version == CPP_SOCKETS_ENDPOINT_VERSION) { + std::string addr; + std::uint16_t port; + + archive(addr, port); + CppSockets::IPv4 ipv4(addr.c_str()); + + construct(ipv4, port); + } else { + throw std::runtime_error("FileMapping file format is unsupported"); + } + + } + }; +} diff --git a/include/FileShare/Config/ServerConfig.hpp b/include/FileShare/Config/ServerConfig.hpp index 53b1d99..63b2eff 100644 --- a/include/FileShare/Config/ServerConfig.hpp +++ b/include/FileShare/Config/ServerConfig.hpp @@ -4,13 +4,16 @@ ** Author Francois Michaut ** ** Started on Wed Aug 6 15:09:50 2025 Francois Michaut -** Last update Fri Aug 22 20:41:53 2025 Francois Michaut +** Last update Fri Jun 12 04:06:14 2026 Francois Michaut ** ** ServerConfig.hpp : Server Configuration */ #pragma once +#include "CppSockets/Address.hpp" +#include + #include namespace FileShare { @@ -22,6 +25,7 @@ namespace FileShare { static constexpr std::filesystem::perms SECURE_FOLDER_PERMS = std::filesystem::perms::owner_all; static auto default_private_keys_dir() -> const std::filesystem::path &; + static auto default_endpoint() -> std::shared_ptr; // paths starting with '~/' will have this part replaced by the current user's home directory static auto load(std::filesystem::path config_file = "") -> ServerConfig; @@ -39,6 +43,10 @@ namespace FileShare { [[nodiscard]] auto is_server_disabled() const -> bool { return m_disable_server; } auto set_server_disabled(bool disabled) -> ServerConfig & { m_disable_server = disabled; return *this; } + [[nodiscard]] auto get_server_endpoint() const -> const CppSockets::IEndpoint & { return *m_server_endpoint; } + auto set_server_endpoint(std::shared_ptr endpoint) -> ServerConfig & { m_server_endpoint = std::move(endpoint); return *this; } + auto set_server_endpoint(std::string_view endpoint) -> ServerConfig & { m_server_endpoint = CppSockets::IEndpoint::from_string(endpoint) ; return *this; } + void validate_config() const; private: template @@ -67,7 +75,10 @@ namespace FileShare { // Set this to true if you want an extra layer of security by // preventing external connections. // Note that when you initiate a connection to another server it will - // be able to send commands as well. + // still be able to send commands to you as well. bool m_disable_server = false; + + // When server is enabled, this is the endpoint it listens to. + std::shared_ptr m_server_endpoint = ServerConfig::default_endpoint(); }; } diff --git a/include/FileShare/Error.hpp b/include/FileShare/Error.hpp index e1b8013..391fdca 100644 --- a/include/FileShare/Error.hpp +++ b/include/FileShare/Error.hpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Sun Oct 22 13:22:53 2023 Francois Michaut -** Last update Sun Oct 22 13:27:05 2023 Francois Michaut +** Last update Sat May 16 11:22:42 2026 Francois Michaut ** ** Error.hpp : Generic Error class */ diff --git a/include/FileShare/Errors/ConfigErrors.hpp b/include/FileShare/Errors/ConfigErrors.hpp new file mode 100644 index 0000000..02ecfca --- /dev/null +++ b/include/FileShare/Errors/ConfigErrors.hpp @@ -0,0 +1,27 @@ +/* +** Project LibFileShareProtocol, 2026 +** +** Author Francois Michaut +** +** Started on Thu May 14 04:34:25 2026 Francois Michaut +** Last update Sat May 16 11:16:25 2026 Francois Michaut +** +** ConfigErrors.hpp : Config related errors +*/ + +#pragma once + +#include "FileShare/Error.hpp" + +#include + +namespace FileShare::Errors::Config { + class NotFoundError : public FileShare::Error { + public: + NotFoundError(const char *path); + NotFoundError(std::string path); + + private: + std::string m_path; + }; +} diff --git a/include/FileShare/Peer/Peer.hpp b/include/FileShare/Peer/Peer.hpp index c79e890..53e5a7a 100644 --- a/include/FileShare/Peer/Peer.hpp +++ b/include/FileShare/Peer/Peer.hpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Sun Aug 28 09:23:07 2022 Francois Michaut -** Last update Sat Aug 23 00:08:01 2025 Francois Michaut +** Last update Wed May 27 09:12:59 2026 Francois Michaut ** ** Peer.hpp : Client to communicate with peers using the FileShareProtocol */ @@ -34,8 +34,9 @@ namespace FileShare { // TODO: Allow copy ? What would that even mean ? Peer(const Peer &) = delete; - Peer(Peer &&) = default; auto operator=(const Peer &) -> Peer & = delete; + + Peer(Peer &&) = default; auto operator=(Peer &&) -> Peer & = default; ~Peer() override = default; diff --git a/include/FileShare/Server.hpp b/include/FileShare/Server.hpp index e29f462..c72b349 100644 --- a/include/FileShare/Server.hpp +++ b/include/FileShare/Server.hpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Mon Aug 29 19:01:51 2022 Francois Michaut -** Last update Fri Aug 22 00:39:26 2025 Francois Michaut +** Last update Fri Jun 12 01:04:34 2026 Francois Michaut ** ** Server.hpp : Server part used to receive qnd process requests of Peers */ @@ -35,7 +35,7 @@ namespace FileShare { public: class Event { public: - enum Type { + enum Type : std::uint8_t { NONE, CONNECT, REQUEST @@ -53,6 +53,10 @@ namespace FileShare { auto type() -> Type & { return m_type; } auto peer() -> PeerBase_ptr & { return m_peer; } auto request() -> std::optional & { return m_request; } + + [[nodiscard]] auto is_request() const -> bool { return m_type == REQUEST; } + [[nodiscard]] auto is_connect() const -> bool { return m_type == CONNECT; } + [[nodiscard]] auto is_none() const -> bool { return m_type == NONE; } private: Type m_type = NONE; PeerBase_ptr m_peer; @@ -71,7 +75,7 @@ namespace FileShare { using FdVector = std::vector; Server( - std::shared_ptr server_endpoint = Server::default_endpoint(), + std::shared_ptr server_endpoint = ServerConfig::default_endpoint(), ServerConfig config = Server::default_config(), Config peer_config = Server::default_peer_config() ); @@ -100,30 +104,30 @@ namespace FileShare { void accept_peer(PreAuthPeer_ptr peer, bool temporary_trust = false); - auto get_config() -> ServerConfig & { return m_config; } - auto get_config() const -> const ServerConfig & { return m_config; } + [[nodiscard]] auto get_config() -> ServerConfig & { return m_config; } + [[nodiscard]] auto get_config() const -> const ServerConfig & { return m_config; } void set_config(const ServerConfig &config) { m_config = config; } // Warning : changing the default peer configuration does NOT // change the already connected Peers, only new ones. You need to // manually update the configuration of each existing peer. - auto get_peer_config() -> Config & { return m_peer_config; } - auto get_peer_config() const -> const Config & { return m_peer_config; } + [[nodiscard]] auto get_peer_config() -> Config & { return m_peer_config; } + [[nodiscard]] auto get_peer_config() const -> const Config & { return m_peer_config; } void set_peer_config(const Config &config) { m_peer_config = config; } static auto default_config() -> ServerConfig; static auto default_peer_config() -> Config; - auto get_server_endpoint() const -> const CppSockets::IEndpoint & { return *m_server_endpoint; } - auto get_socket() const -> const CppSockets::TlsSocket & { return m_socket; } + [[nodiscard]] auto get_server_endpoint() const -> const CppSockets::IEndpoint & { return *m_server_endpoint; } + [[nodiscard]] auto get_socket() const -> const CppSockets::TlsSocket & { return m_socket; } - auto get_peers() const -> const PeerMap & { return m_peers; } - auto get_pending_peers() const -> const PreAuthPeerMap & { return m_pending_authorization_peers; } + [[nodiscard]] auto get_peers() const -> const PeerMap & { return m_peers; } + [[nodiscard]] auto get_pending_peers() const -> const PreAuthPeerMap & { return m_pending_authorization_peers; } - auto get_poll_fds() const -> const FdVector & { return m_fds; } + [[nodiscard]] auto get_poll_fds() const -> const FdVector & { return m_fds; } void restart(); - auto disabled() const -> bool { return m_config.is_server_disabled(); } + [[nodiscard]] auto disabled() const -> bool { return m_config.is_server_disabled(); } void set_disabled(bool disabled); private: void initialize_private_key(); @@ -135,8 +139,6 @@ namespace FileShare { auto insert_peer(PreAuthPeer_ptr &&peer) -> Peer_ptr &; auto insert_peer(Peer_ptr peer) -> Peer_ptr &; - static auto default_endpoint() -> std::shared_ptr; - std::shared_ptr m_server_endpoint; CppSockets::TlsContext m_ctx; CppSockets::TlsSocket m_socket; diff --git a/source/Config/Config.cpp b/source/Config/Config.cpp index adc084f..feb45e2 100644 --- a/source/Config/Config.cpp +++ b/source/Config/Config.cpp @@ -4,18 +4,18 @@ ** Author Francois Michaut ** ** Started on Tue Sep 13 11:29:35 2022 Francois Michaut -** Last update Sun Aug 24 19:46:07 2025 Francois Michaut +** Last update Fri Jun 12 03:00:33 2026 Francois Michaut ** ** FileShareConfig.cpp : FileShareConfig implementation */ #include "FileShare/Config/Config.hpp" #include "FileShare/Config/Serialization.hpp" // NOLINT(misc-include-cleaner,unused-includes) +#include "FileShare/Errors/ConfigErrors.hpp" #include "FileShare/Utils/Path.hpp" #include -#include #include #include @@ -42,9 +42,16 @@ namespace FileShare { Config config(false); config.m_filepath = FileShare::Utils::resolve_home_component(config_file); - std::ifstream file(config.m_filepath, std::ios_base::binary | std::ios_base::in); + + std::ifstream file(config.m_filepath, + std::ios_base::binary | std::ios_base::in); + cereal::BinaryInputArchive archive(file); + if (!file.is_open()) { + throw FileShare::Errors::Config::NotFoundError(config_file.string()); + } + archive(config); // TODO: Need to validate config - if someone messes up the file, we could have problems return config; diff --git a/source/Config/ServerConfig.cpp b/source/Config/ServerConfig.cpp index ded6846..84dbc5a 100644 --- a/source/Config/ServerConfig.cpp +++ b/source/Config/ServerConfig.cpp @@ -4,13 +4,15 @@ ** Author Francois Michaut ** ** Started on Wed Aug 6 15:19:24 2025 Francois Michaut -** Last update Sun Aug 24 19:48:12 2025 Francois Michaut +** Last update Sat Jun 20 09:42:39 2026 Francois Michaut ** ** ServerConfig.cpp : Server Configuration Implementation */ +#include "CppSockets/IPv4.hpp" #include "FileShare/Config/Serialization.hpp" // NOLINT(misc-include-cleaner,unused-includes) #include "FileShare/Config/ServerConfig.hpp" +#include "FileShare/Errors/ConfigErrors.hpp" #include "FileShare/Utils/Path.hpp" #include @@ -20,12 +22,17 @@ #include #include +// Need to be registered after we include the archives it will be used with +CEREAL_REGISTER_TYPE(CppSockets::EndpointV4); + const char * const DEFAULT_PATH = "~/.fsp/server_config"; namespace FileShare { ServerConfig::ServerConfig() : m_filepath(FileShare::Utils::resolve_home_component(DEFAULT_PATH)), - m_uuid("0000-0000-0000-0000") // TODO: Generate Random UUID + m_uuid("0000-0000-0000-0000"), // TODO: Generate Random UUID + m_device_name("Unnamed Device") // TODO: Prevent this from happening. Either generate a random name or ask the user to input one on first launch + // Currently only happens if default_config arg is used {} ServerConfig::ServerConfig(std::string uuid) : m_uuid(std::move(uuid)) {} @@ -36,6 +43,11 @@ namespace FileShare { return private_keys_dir; } + auto ServerConfig::default_endpoint() -> std::shared_ptr { + // TODO: choose a better port than 12345 + return std::make_shared(CppSockets::IPv4("0.0.0.0"), 12345); + } + auto ServerConfig::set_private_keys_dir(std::string_view path) -> ServerConfig & { m_private_keys_dir = FileShare::Utils::resolve_home_component(path); return *this; @@ -57,11 +69,14 @@ namespace FileShare { // Avoid the default constructor which would wastefuly generate an UUID ServerConfig config("0000-0000-0000-0000"); config.m_filepath = FileShare::Utils::resolve_home_component(config_file); - std::ifstream file(config.m_filepath, std::ios_base::binary | std::ios_base::in); + std::ifstream file(config.m_filepath, + std::ios_base::binary | std::ios_base::in); cereal::BinaryInputArchive archive(file); - // TODO: Will crash if file doesn't exist. Do we want to create it ? - // It could also be an error if the config is missing -> TBD + if (!file.is_open()) { + throw FileShare::Errors::Config::NotFoundError(config_file.string()); + } + archive(config); // TODO: Need to validate config - if someone messes up the file, we could have problems // (Or if private_keys_dir contains ~, it needs to be expanded) diff --git a/source/Errors/ConfigErrors.cpp b/source/Errors/ConfigErrors.cpp new file mode 100644 index 0000000..a7941b5 --- /dev/null +++ b/source/Errors/ConfigErrors.cpp @@ -0,0 +1,22 @@ +/* +** Project LibFileShareProtocol, 2026 +** +** Author Francois Michaut +** +** Started on Thu May 14 04:40:23 2026 Francois Michaut +** Last update Thu May 14 04:41:08 2026 Francois Michaut +** +** ConfigErrors.cpp : Config related errors implementation +*/ + +#include "FileShare/Errors/ConfigErrors.hpp" + +namespace FileShare::Errors::Config { + NotFoundError::NotFoundError(const char *path) : + NotFoundError(std::string(path)) + {} + + NotFoundError::NotFoundError(std::string path) : + Error("Config file '" + path + "' not found"), m_path(std::move(path)) + {} +} diff --git a/source/Peer/Peer.cpp b/source/Peer/Peer.cpp index 37187c9..751f163 100644 --- a/source/Peer/Peer.cpp +++ b/source/Peer/Peer.cpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Mon Aug 29 20:50:53 2022 Francois Michaut -** Last update Sat Aug 23 12:59:50 2025 Francois Michaut +** Last update Wed May 27 12:40:37 2026 Francois Michaut ** ** Peer.cpp : Implementation of the FileShareProtocol Client */ @@ -64,14 +64,14 @@ namespace FileShare { return; } case Protocol::CommandCode::SEND_FILE: { - std::shared_ptr data = std::dynamic_pointer_cast(request.request); + auto data = std::dynamic_pointer_cast(request.request); create_download(request.message_id, data); return; // create_download already sends reply to request } case Protocol::CommandCode::RECEIVE_FILE: { - std::shared_ptr data = std::dynamic_pointer_cast(request.request); + auto data = std::dynamic_pointer_cast(request.request); auto virtual_path = data->filepath; auto host_path = m_config.get_file_mapping().virtual_to_host(virtual_path); auto [handler, status] = prepare_upload(host_path.string(), virtual_path, data->packet_size, data->packet_start); diff --git a/source/Server.cpp b/source/Server.cpp index 4e8dd77..af67c45 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -4,7 +4,7 @@ ** Author Francois Michaut ** ** Started on Sun Nov 6 21:06:10 2022 Francois Michaut -** Last update Sun Aug 24 20:16:50 2025 Francois Michaut +** Last update Fri Jun 12 00:50:52 2026 Francois Michaut ** ** Server.cpp : Server implementation */ @@ -116,7 +116,7 @@ namespace FileShare { static constexpr int VERIFY_MODE = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE; Server::Server(ServerConfig config, Config peer_config) : - Server(Server::default_endpoint(), std::move(config), std::move(peer_config)) + Server(ServerConfig::default_endpoint(), std::move(config), std::move(peer_config)) {} Server::Server(std::shared_ptr server_endpoint, ServerConfig config, Config peer_config) : @@ -226,11 +226,6 @@ namespace FileShare { return {}; // TODO: explicitely set default params } - auto Server::default_endpoint() -> std::shared_ptr { - // TODO: choose a better port than 12345 - return std::make_shared(CppSockets::IPv4("0.0.0.0"), 12345); - } - void Server::accept_peer(PreAuthPeer_ptr peer, bool temporary_trust) { if (!temporary_trust) { // Add to known hosts