diff --git a/config/src/comments.rs b/config/src/comments.rs index b277c6166..06c8bd88c 100644 --- a/config/src/comments.rs +++ b/config/src/comments.rs @@ -260,6 +260,7 @@ fn comments() -> HashMap { retval.insert( "seeding_type".to_string(), " +#All seeds/peers can be either IP address or DNS names. Port number must always be specified #how to seed this server, can be None, List or DNSSeed " .to_string(), diff --git a/p2p/src/msg.rs b/p2p/src/msg.rs index 23d879642..aa976b539 100644 --- a/p2p/src/msg.rs +++ b/p2p/src/msg.rs @@ -466,7 +466,7 @@ impl Readable for GetPeerAddrs { /// Peer addresses we know of that are fresh enough, in response to /// GetPeerAddrs. -#[derive(Debug)] +#[derive(Debug, Clone, Serialize, PartialEq)] pub struct PeerAddrs { pub peers: Vec, } @@ -493,7 +493,7 @@ impl Readable for PeerAddrs { for _ in 0..peer_count { peers.push(PeerAddr::read(reader)?); } - Ok(PeerAddrs { peers: peers }) + Ok(PeerAddrs { peers }) } } diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs index a14c1566c..b0bdc1f1c 100644 --- a/p2p/src/peer.rs +++ b/p2p/src/peer.rs @@ -152,7 +152,7 @@ impl Peer { pub fn is_denied(config: &P2PConfig, peer_addr: PeerAddr) -> bool { if let Some(ref denied) = config.peers_deny { - if denied.contains(&peer_addr) { + if denied.peers.contains(&peer_addr) { debug!( "checking peer allowed/denied: {:?} explicitly denied", peer_addr @@ -161,7 +161,7 @@ impl Peer { } } if let Some(ref allowed) = config.peers_allow { - if allowed.contains(&peer_addr) { + if allowed.peers.contains(&peer_addr) { debug!( "checking peer allowed/denied: {:?} explicitly allowed", peer_addr diff --git a/p2p/src/types.rs b/p2p/src/types.rs index 38bf7d450..b455984fc 100644 --- a/p2p/src/types.rs +++ b/p2p/src/types.rs @@ -12,15 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::util::RwLock; use std::convert::From; +use std::fmt; use std::fs::File; use std::io::{self, Read}; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use chrono::prelude::*; +use serde::de::{SeqAccess, Visitor}; +use serde::{Deserialize, Deserializer}; + +use grin_store; use crate::chain; use crate::core::core; @@ -28,7 +33,8 @@ use crate::core::core::hash::Hash; use crate::core::global; use crate::core::pow::Difficulty; use crate::core::ser::{self, ProtocolVersion, Readable, Reader, Writeable, Writer}; -use grin_store; +use crate::msg::PeerAddrs; +use crate::util::RwLock; /// Maximum number of block headers a peer should ever send pub const MAX_BLOCK_HEADERS: u32 = 512; @@ -156,6 +162,45 @@ impl Readable for PeerAddr { } } +impl<'de> Visitor<'de> for PeerAddrs { + type Value = PeerAddrs; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an array of dns names or IP addresses") + } + + fn visit_seq(self, mut access: M) -> Result + where + M: SeqAccess<'de>, + { + let mut peers = Vec::with_capacity(access.size_hint().unwrap_or(0)); + + while let Some(entry) = access.next_element::<&str>()? { + match SocketAddr::from_str(entry) { + // Try to parse IP address first + Ok(ip) => peers.push(PeerAddr(ip)), + // If that fails it's probably a DNS record + Err(_) => { + let socket_addrs = entry + .to_socket_addrs() + .expect(format!("Unable to resolve DNS: {}", entry).as_str()); + peers.append(&mut socket_addrs.map(|addr| PeerAddr(addr)).collect()); + } + } + } + Ok(PeerAddrs { peers }) + } +} + +impl<'de> Deserialize<'de> for PeerAddrs { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(PeerAddrs { peers: vec![] }) + } +} + impl std::hash::Hash for PeerAddr { /// If loopback address then we care about ip and port. /// If regular address then we only care about the ip and ignore the port. @@ -218,18 +263,18 @@ pub struct P2PConfig { pub seeding_type: Seeding, /// The list of seed nodes, if using Seeding as a seed type - pub seeds: Option>, + pub seeds: Option, /// Capabilities expose by this node, also conditions which other peers this /// node will have an affinity toward when connection. pub capabilities: Capabilities, - pub peers_allow: Option>, + pub peers_allow: Option, - pub peers_deny: Option>, + pub peers_deny: Option, /// The list of preferred peers that we will try to connect to - pub peers_preferred: Option>, + pub peers_preferred: Option, pub ban_window: Option, @@ -316,7 +361,7 @@ impl P2PConfig { pub enum Seeding { /// No seeding, mostly for tests that programmatically connect None, - /// A list of seed addresses provided to the server + /// A list of seeds provided to the server (can be addresses or DNS names) List, /// Automatically get a list of seeds from multiple DNS DNSSeed, diff --git a/servers/src/grin/seed.rs b/servers/src/grin/seed.rs index 7921429a0..7ca38bc80 100644 --- a/servers/src/grin/seed.rs +++ b/servers/src/grin/seed.rs @@ -361,35 +361,47 @@ fn listen_for_addrs( } } -pub fn dns_seeds() -> Box Vec + Send> { +pub fn default_dns_seeds() -> Box Vec + Send> { Box::new(|| { - let mut addresses: Vec = vec![]; let net_seeds = if global::is_floonet() { FLOONET_DNS_SEEDS } else { MAINNET_DNS_SEEDS }; - for dns_seed in net_seeds { - let temp_addresses = addresses.clone(); - debug!("Retrieving seed nodes from dns {}", dns_seed); - match (dns_seed.to_owned(), 0).to_socket_addrs() { - Ok(addrs) => addresses.append( - &mut (addrs - .map(|mut addr| { - addr.set_port(if global::is_floonet() { 13414 } else { 3414 }); - PeerAddr(addr) - }) - .filter(|addr| !temp_addresses.contains(addr)) - .collect()), - ), - Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e), - } - } - debug!("Retrieved seed addresses: {:?}", addresses); - addresses + resolve_dns_to_addrs( + &net_seeds + .iter() + .map(|s| { + s.to_string() + + if global::is_floonet() { + ":13414" + } else { + ":3414" + } + }) + .collect(), + ) }) } +fn resolve_dns_to_addrs(dns_records: &Vec) -> Vec { + let mut addresses: Vec = vec![]; + for dns in dns_records { + debug!("Retrieving addresses from dns {}", dns); + match dns.to_socket_addrs() { + Ok(addrs) => addresses.append( + &mut addrs + .map(|addr| PeerAddr(addr)) + .filter(|addr| !addresses.contains(addr)) + .collect(), + ), + Err(e) => debug!("Failed to resolve dns {:?} got error {:?}", dns, e), + }; + } + debug!("Resolved addresses: {:?}", addresses); + addresses +} + /// Convenience function when the seed list is immediately known. Mostly used /// for tests. pub fn predefined_seeds(addrs: Vec) -> Box Vec + Send> { diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 6e5629965..61df288bc 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -231,22 +231,24 @@ impl Server { seed::predefined_seeds(vec![]) } p2p::Seeding::List => match &config.p2p_config.seeds { - Some(seeds) => seed::predefined_seeds(seeds.clone()), + Some(seeds) => seed::predefined_seeds(seeds.peers.clone()), None => { return Err(Error::Configuration( "Seeds must be configured for seeding type List".to_owned(), )); } }, - p2p::Seeding::DNSSeed => seed::dns_seeds(), + p2p::Seeding::DNSSeed => seed::default_dns_seeds(), _ => unreachable!(), }; + let preferred_peers = config.p2p_config.peers_preferred.clone().map(|p| p.peers); + connect_thread = Some(seed::connect_and_monitor( p2p_server.clone(), config.p2p_config.capabilities, seeder, - config.p2p_config.peers_preferred.clone(), + preferred_peers, stop_state.clone(), )?); } diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 1b1484c09..9754ba7fd 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -24,9 +24,11 @@ use ctrlc; use crate::config::GlobalConfig; use crate::core::global; -use crate::p2p::{PeerAddr, Seeding}; +use crate::p2p::Seeding; use crate::servers; use crate::tui::ui; +use grin_p2p::msg::PeerAddrs; +use grin_p2p::PeerAddr; use grin_util::logger::LogEntry; use std::sync::mpsc; @@ -119,12 +121,12 @@ pub fn server_command( } if let Some(seeds) = a.values_of("seed") { - let seed_addrs = seeds - .filter_map(|x| x.parse().ok()) - .map(|x| PeerAddr(x)) + let peers = seeds + .filter_map(|s| s.parse().ok()) + .map(|sa| PeerAddr(sa)) .collect(); server_config.p2p_config.seeding_type = Seeding::List; - server_config.p2p_config.seeds = Some(seed_addrs); + server_config.p2p_config.seeds = Some(PeerAddrs { peers }); } }