mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
feat: allow DNS names in peers/seeds list and resolve them (#3125)
* feat: allow DNS names in peers/seeds list and resolve them * tests: add mod for peer tests * refactor: rename some variables * chore: use Serde desrialize to resolve DNS names into PeerAddrs * fix: compile * fix: add back code to remove duplicate ip addresses from resolved DNS seeds
This commit is contained in:
parent
053415ddf8
commit
0d2e58e90e
7 changed files with 102 additions and 40 deletions
|
@ -260,6 +260,7 @@ fn comments() -> HashMap<String, String> {
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"seeding_type".to_string(),
|
"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
|
#how to seed this server, can be None, List or DNSSeed
|
||||||
"
|
"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
|
|
@ -466,7 +466,7 @@ impl Readable for GetPeerAddrs {
|
||||||
|
|
||||||
/// Peer addresses we know of that are fresh enough, in response to
|
/// Peer addresses we know of that are fresh enough, in response to
|
||||||
/// GetPeerAddrs.
|
/// GetPeerAddrs.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||||
pub struct PeerAddrs {
|
pub struct PeerAddrs {
|
||||||
pub peers: Vec<PeerAddr>,
|
pub peers: Vec<PeerAddr>,
|
||||||
}
|
}
|
||||||
|
@ -493,7 +493,7 @@ impl Readable for PeerAddrs {
|
||||||
for _ in 0..peer_count {
|
for _ in 0..peer_count {
|
||||||
peers.push(PeerAddr::read(reader)?);
|
peers.push(PeerAddr::read(reader)?);
|
||||||
}
|
}
|
||||||
Ok(PeerAddrs { peers: peers })
|
Ok(PeerAddrs { peers })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ impl Peer {
|
||||||
|
|
||||||
pub fn is_denied(config: &P2PConfig, peer_addr: PeerAddr) -> bool {
|
pub fn is_denied(config: &P2PConfig, peer_addr: PeerAddr) -> bool {
|
||||||
if let Some(ref denied) = config.peers_deny {
|
if let Some(ref denied) = config.peers_deny {
|
||||||
if denied.contains(&peer_addr) {
|
if denied.peers.contains(&peer_addr) {
|
||||||
debug!(
|
debug!(
|
||||||
"checking peer allowed/denied: {:?} explicitly denied",
|
"checking peer allowed/denied: {:?} explicitly denied",
|
||||||
peer_addr
|
peer_addr
|
||||||
|
@ -161,7 +161,7 @@ impl Peer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ref allowed) = config.peers_allow {
|
if let Some(ref allowed) = config.peers_allow {
|
||||||
if allowed.contains(&peer_addr) {
|
if allowed.peers.contains(&peer_addr) {
|
||||||
debug!(
|
debug!(
|
||||||
"checking peer allowed/denied: {:?} explicitly allowed",
|
"checking peer allowed/denied: {:?} explicitly allowed",
|
||||||
peer_addr
|
peer_addr
|
||||||
|
|
|
@ -12,15 +12,20 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::util::RwLock;
|
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read};
|
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::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
use serde::de::{SeqAccess, Visitor};
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
use grin_store;
|
||||||
|
|
||||||
use crate::chain;
|
use crate::chain;
|
||||||
use crate::core::core;
|
use crate::core::core;
|
||||||
|
@ -28,7 +33,8 @@ use crate::core::core::hash::Hash;
|
||||||
use crate::core::global;
|
use crate::core::global;
|
||||||
use crate::core::pow::Difficulty;
|
use crate::core::pow::Difficulty;
|
||||||
use crate::core::ser::{self, ProtocolVersion, Readable, Reader, Writeable, Writer};
|
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
|
/// Maximum number of block headers a peer should ever send
|
||||||
pub const MAX_BLOCK_HEADERS: u32 = 512;
|
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<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 {
|
impl std::hash::Hash for PeerAddr {
|
||||||
/// If loopback address then we care about ip and port.
|
/// If loopback address then we care about ip and port.
|
||||||
/// If regular address then we only care about the ip and ignore the 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,
|
pub seeding_type: Seeding,
|
||||||
|
|
||||||
/// The list of seed nodes, if using Seeding as a seed type
|
/// 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
|
/// Capabilities expose by this node, also conditions which other peers this
|
||||||
/// node will have an affinity toward when connection.
|
/// node will have an affinity toward when connection.
|
||||||
pub capabilities: Capabilities,
|
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
|
/// 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>,
|
pub ban_window: Option<i64>,
|
||||||
|
|
||||||
|
@ -316,7 +361,7 @@ impl P2PConfig {
|
||||||
pub enum Seeding {
|
pub enum Seeding {
|
||||||
/// No seeding, mostly for tests that programmatically connect
|
/// No seeding, mostly for tests that programmatically connect
|
||||||
None,
|
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,
|
List,
|
||||||
/// Automatically get a list of seeds from multiple DNS
|
/// Automatically get a list of seeds from multiple DNS
|
||||||
DNSSeed,
|
DNSSeed,
|
||||||
|
|
|
@ -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(|| {
|
Box::new(|| {
|
||||||
let mut addresses: Vec<PeerAddr> = vec![];
|
|
||||||
let net_seeds = if global::is_floonet() {
|
let net_seeds = if global::is_floonet() {
|
||||||
FLOONET_DNS_SEEDS
|
FLOONET_DNS_SEEDS
|
||||||
} else {
|
} else {
|
||||||
MAINNET_DNS_SEEDS
|
MAINNET_DNS_SEEDS
|
||||||
};
|
};
|
||||||
for dns_seed in net_seeds {
|
resolve_dns_to_addrs(
|
||||||
let temp_addresses = addresses.clone();
|
&net_seeds
|
||||||
debug!("Retrieving seed nodes from dns {}", dns_seed);
|
.iter()
|
||||||
match (dns_seed.to_owned(), 0).to_socket_addrs() {
|
.map(|s| {
|
||||||
Ok(addrs) => addresses.append(
|
s.to_string()
|
||||||
&mut (addrs
|
+ if global::is_floonet() {
|
||||||
.map(|mut addr| {
|
":13414"
|
||||||
addr.set_port(if global::is_floonet() { 13414 } else { 3414 });
|
} else {
|
||||||
PeerAddr(addr)
|
":3414"
|
||||||
})
|
}
|
||||||
.filter(|addr| !temp_addresses.contains(addr))
|
})
|
||||||
.collect()),
|
.collect(),
|
||||||
),
|
)
|
||||||
Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug!("Retrieved seed addresses: {:?}", addresses);
|
|
||||||
addresses
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/// Convenience function when the seed list is immediately known. Mostly used
|
||||||
/// for tests.
|
/// for tests.
|
||||||
pub fn predefined_seeds(addrs: Vec<PeerAddr>) -> Box<dyn Fn() -> Vec<PeerAddr> + Send> {
|
pub fn predefined_seeds(addrs: Vec<PeerAddr>) -> Box<dyn Fn() -> Vec<PeerAddr> + Send> {
|
||||||
|
|
|
@ -231,22 +231,24 @@ impl Server {
|
||||||
seed::predefined_seeds(vec![])
|
seed::predefined_seeds(vec![])
|
||||||
}
|
}
|
||||||
p2p::Seeding::List => match &config.p2p_config.seeds {
|
p2p::Seeding::List => match &config.p2p_config.seeds {
|
||||||
Some(seeds) => seed::predefined_seeds(seeds.clone()),
|
Some(seeds) => seed::predefined_seeds(seeds.peers.clone()),
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::Configuration(
|
return Err(Error::Configuration(
|
||||||
"Seeds must be configured for seeding type List".to_owned(),
|
"Seeds must be configured for seeding type List".to_owned(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
p2p::Seeding::DNSSeed => seed::dns_seeds(),
|
p2p::Seeding::DNSSeed => seed::default_dns_seeds(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let preferred_peers = config.p2p_config.peers_preferred.clone().map(|p| p.peers);
|
||||||
|
|
||||||
connect_thread = Some(seed::connect_and_monitor(
|
connect_thread = Some(seed::connect_and_monitor(
|
||||||
p2p_server.clone(),
|
p2p_server.clone(),
|
||||||
config.p2p_config.capabilities,
|
config.p2p_config.capabilities,
|
||||||
seeder,
|
seeder,
|
||||||
config.p2p_config.peers_preferred.clone(),
|
preferred_peers,
|
||||||
stop_state.clone(),
|
stop_state.clone(),
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,11 @@ use ctrlc;
|
||||||
|
|
||||||
use crate::config::GlobalConfig;
|
use crate::config::GlobalConfig;
|
||||||
use crate::core::global;
|
use crate::core::global;
|
||||||
use crate::p2p::{PeerAddr, Seeding};
|
use crate::p2p::Seeding;
|
||||||
use crate::servers;
|
use crate::servers;
|
||||||
use crate::tui::ui;
|
use crate::tui::ui;
|
||||||
|
use grin_p2p::msg::PeerAddrs;
|
||||||
|
use grin_p2p::PeerAddr;
|
||||||
use grin_util::logger::LogEntry;
|
use grin_util::logger::LogEntry;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
@ -119,12 +121,12 @@ pub fn server_command(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(seeds) = a.values_of("seed") {
|
if let Some(seeds) = a.values_of("seed") {
|
||||||
let seed_addrs = seeds
|
let peers = seeds
|
||||||
.filter_map(|x| x.parse().ok())
|
.filter_map(|s| s.parse().ok())
|
||||||
.map(|x| PeerAddr(x))
|
.map(|sa| PeerAddr(sa))
|
||||||
.collect();
|
.collect();
|
||||||
server_config.p2p_config.seeding_type = Seeding::List;
|
server_config.p2p_config.seeding_type = Seeding::List;
|
||||||
server_config.p2p_config.seeds = Some(seed_addrs);
|
server_config.p2p_config.seeds = Some(PeerAddrs { peers });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue