BastionGuard/thirdparty/bastionguard-secure-connection/common/include/bsc/proto/Wire.hpp
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

91 lines
3.7 KiB
C++

/*
* BastionGuard™ Secure Connection
* Copyright (C) 2025–2026 Calogero Scarnà
* GPL-3.0
*
* Protocollo wire daemon ↔ UI.
*
* Trasporto: Unix domain socket (SOCK_STREAM).
* Framing: 4 byte little-endian length (uint32) + payload JSON UTF-8.
* Nessun terminatore, nessun escape: la lunghezza e' l'unico marker.
*
* La UI e' sempre il server. Il daemon si connette come client.
*
* Ogni messaggio ha due campi obbligatori:
* "type": stringa, identifica il tipo di messaggio
* "id": uint64, correlazione per richieste/risposte
*
* Direzione daemon → UI:
* hello -> primo messaggio, { type, id, daemon_version }
* ping -> heartbeat, { type, id, stats: {...} }
* ask_rule -> richiede decisione utente, { type, id, connection: {...} }
* La UI risponde con ask_rule_reply con stesso id.
* alert -> notifica informativa, { type, id, level, what, text, connection? }
*
* Direzione UI → daemon:
* pong -> risposta a ping, { type, id }
* ask_rule_reply -> risposta a ask_rule, { type, id, action, duration, rule_name? }
* notification -> notifica runtime, { type, id, kind, data? }
* kind ∈ { stop, reload_rules, change_config, enable_interception,
* disable_interception, log_level, ... }
*/
#pragma once
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
#include <vector>
#include <optional>
namespace bsc::proto {
// Path di default del socket Unix della UI.
inline constexpr const char* kDefaultSocketPath = "/tmp/bsd-daemon.sock";
// Tipi di messaggi.
namespace msg {
inline constexpr std::string_view kHello = "hello";
inline constexpr std::string_view kPing = "ping";
inline constexpr std::string_view kPong = "pong";
inline constexpr std::string_view kAskRule = "ask_rule";
inline constexpr std::string_view kAskRuleReply = "ask_rule_reply";
inline constexpr std::string_view kAlert = "alert";
inline constexpr std::string_view kNotification = "notification";
} // namespace msg
// Risoluzione socketPath: accetta sia "/tmp/foo.sock" sia "unix:///tmp/foo.sock".
inline std::string resolve_socket_path(std::string uri) {
static const std::string prefix = "unix://";
if (uri.rfind(prefix, 0) == 0) uri = uri.substr(prefix.size());
return uri;
}
// Codifica un payload JSON in un frame [uint32 LE length][payload]. Inserisce
// nel vettore `out` senza allocare di piu'.
inline void frame_encode(std::string_view payload, std::vector<std::uint8_t>& out) {
const std::uint32_t len = static_cast<std::uint32_t>(payload.size());
out.resize(4 + payload.size());
out[0] = static_cast<std::uint8_t>(len & 0xff);
out[1] = static_cast<std::uint8_t>((len >> 8) & 0xff);
out[2] = static_cast<std::uint8_t>((len >> 16) & 0xff);
out[3] = static_cast<std::uint8_t>((len >> 24) & 0xff);
std::memcpy(out.data() + 4, payload.data(), payload.size());
}
// Legge 4 byte di lunghezza dal buffer. Restituisce -1 se il buffer non ha
// almeno 4 byte.
inline std::int64_t frame_peek_length(const std::uint8_t* buf, std::size_t len) {
if (len < 4) return -1;
return static_cast<std::uint32_t>(buf[0])
| (static_cast<std::uint32_t>(buf[1]) << 8)
| (static_cast<std::uint32_t>(buf[2]) << 16)
| (static_cast<std::uint32_t>(buf[3]) << 24);
}
// Dimensione massima accettabile di un frame (DoS guard). 4 MiB e' ampiamente
// sufficiente per i messaggi del protocollo: AskRule col process tree piu'
// grande mai visto sta sotto i 64 KiB.
inline constexpr std::size_t kMaxFrameSize = 4u * 1024u * 1024u;
} // namespace bsc::proto