#pragma once #include "bsc/config/Config.hpp" #include "bsc/fw/Firewall.hpp" #include "bsc/net/DnsResolver.hpp" #include "bsc/netlink/ProcEventMonitor.hpp" #include "bsc/net/SocketSnapshot.hpp" #include "bsc/netfilter/NFQueue.hpp" #include "bsc/proc/ProcMon.hpp" #include "bsc/rule/Rule.hpp" #include "bsc/stats/Statistics.hpp" #include "bsc/task/TaskRunner.hpp" #include "bsc/ui/UIClient.hpp" #include #include #include #include #include #include #include namespace bsc::service { class DaemonService { public: explicit DaemonService(bsc::config::Config config); void run(); void stop(); private: void scan_connections(); bool desktop_allows_activity() const; void set_desktop_active(bool active); void handle_proc_event(const bsc::netlink::ProcEvent& ev); void heartbeat(); void reload_rules(); void reload_firewall(); void handle_notification(const bsc::ui::NotificationEvent& ev); bsc::netfilter::Decision decide_connection(bsc::model::Connection conn, bool realtime, bool allow_blocking_ask = true); void persist_learned_rule(const bsc::model::Connection& conn, const bsc::rule::Rule& decision); void remember_connection(const bsc::model::Connection& conn); void enrich_from_recent_flows(bsc::model::Connection& conn); void enrich_from_recent_processes(bsc::model::Connection& conn, std::chrono::milliseconds max_age = std::chrono::milliseconds(5000)); bool rules_changed() const; static std::chrono::milliseconds parse_interval(const std::string& v, std::chrono::milliseconds def = std::chrono::seconds(5)); bsc::config::Config config_; bsc::fw::Firewall firewall_; bsc::net::DnsResolver dns_; bsc::netlink::ProcEventMonitor proc_events_; bsc::net::SocketSnapshot net_; bsc::netfilter::NFQueue nfqueue_; bsc::proc::ProcMon proc_; bsc::rule::Loader rules_; bsc::stats::Statistics stats_; bsc::ui::UIClient ui_; bsc::task::TaskRunner tasks_; std::atomic_bool running_{true}; std::mutex seen_mutex_; std::set seen_; mutable std::mutex rules_reload_mutex_; mutable std::mutex flow_mutex_; std::unordered_map> recent_flows_; // ── Cache verdetti utente ───────────────────────────────────────── // Chiave: "app_path|dst_host|dst_port" (dst_host se presente, altrimenti // dst_ip). Valore: action ("allow"/"deny") + duration + scadenza. // Evita di fare un popup per ogni connessione della stessa app verso lo // stesso host — necessario o la UI viene sommersa. struct CachedVerdict { std::string action; std::string duration; // "once" | "always" | "until.restart" | "30s" | "5m" ... std::chrono::steady_clock::time_point expires_at; }; mutable std::mutex verdict_cache_mutex_; std::unordered_map verdict_cache_; static std::string verdict_key(const bsc::model::Connection& c); std::optional verdict_lookup(const bsc::model::Connection& c) const; void verdict_store(const bsc::model::Connection& c, const bsc::rule::Rule& r); // ── Flag isAsking ───────────────────────────────────────────────── // Vero mentre un ask_rule e' in volo verso la UI. Le altre connessioni // che arrivano nel frattempo NON aprono altri popup: ricevono il // default_action cosi' il traffico non resta bloccato. Il popup per loro // arrivera' al giro successivo, una alla volta. std::atomic_bool is_asking_{false}; // Stato attività desktop comunicato dalla UI. // False = desktop non pronto/bloccato/idle oppure UI non connessa: // il daemon non scansiona, non apre popup e accetta velocemente i pacchetti. std::atomic_bool desktop_active_{false}; std::atomic_bool inactive_notice_logged_{false}; std::filesystem::file_time_type rules_mtime_{}; std::chrono::steady_clock::time_point start_time_{std::chrono::steady_clock::now()}; }; } // namespace bsc::service