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(
|
||||
"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(),
|
||||
|
|
|
@ -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<PeerAddr>,
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<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.
|
||||
|
@ -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<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>,
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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(),
|
||||
)?);
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue