Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow DNS names in peers/seeds list and resolve them #3125

Merged
merged 8 commits into from
Feb 14, 2020
1 change: 1 addition & 0 deletions config/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ fn comments() -> HashMap<String, String> {
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(),
Expand Down
4 changes: 2 additions & 2 deletions p2p/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,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<PeerAddr>,
}
Expand All @@ -495,7 +495,7 @@ impl Readable for PeerAddrs {
for _ in 0..peer_count {
peers.push(PeerAddr::read(reader)?);
}
Ok(PeerAddrs { peers: peers })
Ok(PeerAddrs { peers })
}
}

Expand Down
4 changes: 2 additions & 2 deletions p2p/src/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
62 changes: 53 additions & 9 deletions p2p/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,30 @@
// 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::mpsc;
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;
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;
Expand Down Expand Up @@ -161,6 +166,45 @@ impl Readable for PeerAddr {
}
}

impl<'de> Visitor<'de> for PeerAddrs {
quentinlesceller marked this conversation as resolved.
Show resolved Hide resolved
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<M>(self, mut access: M) -> Result<Self::Value, M::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
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.
Expand Down Expand Up @@ -223,18 +267,18 @@ pub struct P2PConfig {
pub seeding_type: Seeding,

/// The list of seed nodes, if using Seeding as a seed type
pub seeds: Option<Vec<PeerAddr>>,
pub seeds: Option<PeerAddrs>,

/// 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<Vec<PeerAddr>>,
pub peers_allow: Option<PeerAddrs>,

pub peers_deny: Option<Vec<PeerAddr>>,
pub peers_deny: Option<PeerAddrs>,

/// The list of preferred peers that we will try to connect to
pub peers_preferred: Option<Vec<PeerAddr>>,
pub peers_preferred: Option<PeerAddrs>,

pub ban_window: Option<i64>,

Expand Down Expand Up @@ -321,7 +365,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,
Expand Down
52 changes: 32 additions & 20 deletions servers/src/grin/seed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,35 +361,47 @@ fn listen_for_addrs(
}
}

pub fn dns_seeds() -> Box<dyn Fn() -> Vec<PeerAddr> + Send> {
pub fn default_dns_seeds() -> Box<dyn Fn() -> Vec<PeerAddr> + Send> {
Box::new(|| {
let mut addresses: Vec<PeerAddr> = 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<String>) -> Vec<PeerAddr> {
let mut addresses: Vec<PeerAddr> = 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<PeerAddr>) -> Box<dyn Fn() -> Vec<PeerAddr> + Send> {
Expand Down
8 changes: 5 additions & 3 deletions servers/src/grin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)?);
}
Expand Down
12 changes: 7 additions & 5 deletions src/bin/cmd/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 });
}
}

Expand Down