/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 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::{sync::Arc, time::UNIX_EPOCH}; use async_trait::async_trait; use log::debug; use smol::Executor; use super::{ super::{ channel::ChannelPtr, hosts::store::HostsPtr, message::{AddrsMessage, GetAddrsMessage}, message_subscriber::MessageSubscription, p2p::P2pPtr, settings::SettingsPtr, }, protocol_base::{ProtocolBase, ProtocolBasePtr}, }; use crate::{net::hosts::refinery::ping_node, Result}; /// Implements the seed protocol pub struct ProtocolSeed { channel: ChannelPtr, hosts: HostsPtr, settings: SettingsPtr, addr_sub: MessageSubscription, p2p: P2pPtr, } const PROTO_NAME: &str = "ProtocolSeed"; impl ProtocolSeed { /// Create a new seed protocol. pub async fn init(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr { let hosts = p2p.hosts(); let settings = p2p.settings(); // Create a subscription to address message let addr_sub = channel.subscribe_msg::().await.expect("Missing addr dispatcher!"); Arc::new(Self { channel, hosts, settings, addr_sub, p2p }) } /// Sends own external addresses over a channel. Imports own external addresses /// from settings, then adds those addresses to an addrs message and sends it /// out over the channel. pub async fn send_my_addrs(&self) -> Result<()> { debug!(target: "net::protocol_seed::send_my_addrs()", "[START]"); // Do nothing if external addresses are not configured if self.settings.external_addrs.is_empty() { debug!(target: "net::protocol_seed::send_my_addrs()", "External address is not configured. Stopping"); return Ok(()) } // Do nothing if advertise is set to false if !self.settings.advertise { debug!(target: "net::protocol_seed::send_my_addrs()", "Advertise is set to false. Stopping"); return Ok(()) } let mut addrs = vec![]; for addr in self.settings.external_addrs.clone() { debug!(target: "net::protocol_seed::send_my_addrs()", "Attempting to ping self"); // See if we can do a version exchange with ourself. if ping_node(&addr, self.p2p.clone()).await { // We're online. Update last_seen and broadcast our address. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs(); addrs.push((addr, last_seen)); } else { debug!(target: "net::protocol_seed::send_my_addrs()", "Ping self failed"); return Ok(()) } } debug!(target: "net::protocol_seed::send_my_addrs()", "Broadcasting address"); let ext_addr_msg = AddrsMessage { addrs }; self.channel.send(&ext_addr_msg).await?; debug!(target: "net::protocol_seed::send_my_addrs()", "[END]"); Ok(()) } } #[async_trait] impl ProtocolBase for ProtocolSeed { /// Starts the seed protocol. Creates a subscription to the address message, /// then sends our address to the seed server. Sends a get-address message /// and receives an address messsage. async fn start(self: Arc, _ex: Arc>) -> Result<()> { debug!(target: "net::protocol_seed::start()", "START => address={}", self.channel.address()); // Send own address to the seed server self.send_my_addrs().await?; // Send get address message let get_addr = GetAddrsMessage { max: self.settings.outbound_connections as u32, transports: self.settings.allowed_transports.clone(), }; self.channel.send(&get_addr).await?; // Receive addresses let addrs_msg = self.addr_sub.receive().await?; debug!( target: "net::protocol_seed::start()", "Received {} addrs from {}", addrs_msg.addrs.len(), self.channel.address(), ); debug!( target: "net::protocol_seed::start()", "Appending to greylist...", ); self.hosts.greylist_store_or_update(&addrs_msg.addrs).await?; debug!(target: "net::protocol_seed::start()", "END => address={}", self.channel.address()); Ok(()) } fn name(&self) -> &'static str { PROTO_NAME } }