/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2025 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::{ collections::{HashMap, HashSet}, sync::Arc, }; use log::{debug, error, info}; use smol::lock::Mutex; use darkfi::{ net::settings::Settings, rpc::{ jsonrpc::JsonSubscriber, server::{listen_and_serve, RequestHandler}, settings::RpcSettings, }, system::{ExecutorPtr, StoppableTask, StoppableTaskPtr}, Error, Result, }; /// JSON-RPC server methods mod rpc; /// P2P net protocols mod proto; use proto::{DamP2pHandler, DamP2pHandlerPtr}; /// P2P network flooder mod flooder; use flooder::{DamFlooder, DamFlooderPtr}; /// Atomic pointer to the Denial-of-service Analysis Multitool node pub type DamNodePtr = Arc; /// Structure representing a Denial-of-service Analysis Multitool node pub struct DamNode { /// P2P network protocols handler. p2p_handler: DamP2pHandlerPtr, /// A map of various subscribers exporting live info from the node subscribers: HashMap<&'static str, JsonSubscriber>, /// JSON-RPC connection tracker rpc_connections: Mutex>, /// Network flooder flooder: DamFlooderPtr, } impl DamNode { pub async fn new( p2p_handler: DamP2pHandlerPtr, subscribers: HashMap<&'static str, JsonSubscriber>, flooder: DamFlooderPtr, ) -> DamNodePtr { Arc::new(Self { p2p_handler, subscribers, rpc_connections: Mutex::new(HashSet::new()), flooder, }) } } /// Atomic pointer to the Denial-of-service Analysis Multitool daemon pub type DamdPtr = Arc; /// Structure representing a Denial-of-service Analysis Multitool daemon pub struct Damd { /// Darkfi node instance node: DamNodePtr, /// `dnet` background task dnet_task: StoppableTaskPtr, /// JSON-RPC background task rpc_task: StoppableTaskPtr, } impl Damd { /// Initialize a Denial-of-service Analysis Multitool daemon. /// /// Generates a new `DamNode` for provided configuration, /// along with all the corresponding background tasks. pub async fn init(net_settings: &Settings, ex: &ExecutorPtr) -> Result { info!(target: "damd::Damd::init", "Initializing a Denial-of-service Analysis Multitool daemon..."); // Initialize P2P network let p2p_handler = DamP2pHandler::init(net_settings, ex).await?; // Here we initialize various subscribers that can export live network data. let mut subscribers = HashMap::new(); subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events")); subscribers.insert("foo", JsonSubscriber::new("protocols.subscribe_foo")); subscribers.insert("attack_foo", JsonSubscriber::new("protocols.subscribe_attack_foo")); subscribers.insert("bar", JsonSubscriber::new("protocols.subscribe_bar")); subscribers.insert("attack_bar", JsonSubscriber::new("protocols.subscribe_attack_bar")); // Initialize flooder let flooder = DamFlooder::init(&p2p_handler.p2p, ex); // Initialize node let node = DamNode::new(p2p_handler, subscribers, flooder).await; // Generate the background tasks let dnet_task = StoppableTask::new(); let rpc_task = StoppableTask::new(); info!(target: "damd::Damd::init", "Denial-of-service Analysis Multitool daemon initialized successfully!"); Ok(Arc::new(Self { node, dnet_task, rpc_task })) } /// Start the Denial-of-service Analysis Multitool daemon in the given executor, /// using the provided JSON-RPC configuration. pub async fn start(&self, executor: &ExecutorPtr, rpc_settings: &RpcSettings) -> Result<()> { info!(target: "damd::Damd::start", "Starting Denial-of-service Analysis Multitool daemon..."); // Start the `dnet` task info!(target: "damd::Damd::start", "Starting dnet subs task"); let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone(); let p2p_ = self.node.p2p_handler.p2p.clone(); self.dnet_task.clone().start( async move { let dnet_sub = p2p_.dnet_subscribe().await; loop { let event = dnet_sub.receive().await; debug!(target: "damd::Damd::dnet_task", "Got dnet event: {:?}", event); dnet_sub_.notify(vec![event.into()].into()).await; } }, |res| async { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => { error!(target: "damd::Damd::start", "Failed starting dnet subs task: {}", e) } } }, Error::DetachedTaskStopped, executor.clone(), ); // Start the JSON-RPC task info!(target: "damd::Damd::start", "Starting JSON-RPC server"); let node_ = self.node.clone(); self.rpc_task.clone().start( listen_and_serve(rpc_settings.clone(), self.node.clone(), None, executor.clone()), |res| async move { match res { Ok(()) | Err(Error::RpcServerStopped) => node_.stop_connections().await, Err(e) => error!(target: "damd::Damd::start", "Failed starting JSON-RPC server: {}", e), } }, Error::RpcServerStopped, executor.clone(), ); // Start the P2P network info!(target: "damd::Damd::start", "Starting P2P network"); self.node.p2p_handler.clone().start(executor, &self.node.subscribers).await?; info!(target: "damd::Damd::start", "Denial-of-service Analysis Multitool daemon started successfully!"); Ok(()) } /// Stop the Denial-of-service Analysis Multitool daemon. pub async fn stop(&self) -> Result<()> { info!(target: "damd::Damd::stop", "Terminating Denial-of-service Analysis Multitool daemon..."); // Stop the flooder info!(target: "damd::Damd::stop", "Stopping the flooder..."); self.node.flooder.stop().await; // Stop the `dnet` node info!(target: "damd::Damd::stop", "Stopping dnet subs task..."); self.dnet_task.stop().await; // Stop the JSON-RPC task info!(target: "damd::Damd::stop", "Stopping JSON-RPC server..."); self.rpc_task.stop().await; // Stop the P2P network info!(target: "damd::Damd::stop", "Stopping P2P network protocols handler..."); self.node.p2p_handler.stop().await; info!(target: "damd::Damd::stop", "Denial-of-service Analysis Multitool daemon terminated successfully!"); Ok(()) } }